內存管理
ARC處理原理
ARC是Objective-C編譯器的特性,而不是運行時特性或者垃圾回收機制,ARC所做的只不過是在代碼編譯時為你自動在合適的位置插入release或autorelease,只要沒有強指針指向對象,對象就會被釋放。
前端編譯器
前端編譯器會為“擁有的”每一個對象插入相應的release語句。如果對象的所有權修飾符是__strong,那么它就是被擁有的。如果在某個方法內創建了一個對象,前端編譯器會在方法末尾自動插入release語句以銷毀它。而類擁有的對象(實例變量/屬性)會在dealloc方法內被釋放。事實上,你并不需要寫dealloc方法或調用父類的dealloc方法,ARC會自動幫你完成一切。此外,由編譯器生成的代碼甚至會比你自己寫的release語句的性能還要好,因為編輯器可以作出一些假設。在ARC中,沒有類可以覆蓋release方法,也沒有調用它的必要。ARC會通過直接使用objc_release來優化調用過程。而對于retain也是同樣的方法。ARC會調用objc_retain來取代保留消息。
ARC優化器
雖然前端編譯器聽起來很厲害的樣子,但代碼中有時仍會出現幾個對retain和release的重復調用。ARC優化器負責移除多余的retain和release語句,確保生成的代碼運行速度高于手動引用計數的代碼。
下面關于Objective-C內存管理的描述錯誤的是
A 當使用ARC來管理內存時,代碼中不可以出現autorelease
B autoreleasepool 在 drain 的時候會釋放在其中分配的對象
C 當使用ARC來管理內存時,在線程中大量分配對象而不用autoreleasepool則可能會造成內存泄露
D 在使用ARC的項目中不能使用NSZone
參考答案:A
理由:ARC只是在大多時候編譯自動為我們添加上內存管理的代碼,只是我們的源代碼看不到而已,但是在編譯時,編譯器會添加上相關內存管理代碼。對于自動釋放池,在drain時會將自動釋放池中的所有對象的引用計數減一,若引用計數為0,則會自動釋放掉其內存。如果在線程中需要大量分配內存,我們理應添加上自動釋放池,以防內存泄露。比如在for循環中要分配大量的內存處理數據,那么我們應該在for循環內添加自動釋放池,在每個循環后就將內存釋放掉,防止內存泄露。在ARC項目中,自然不能手動使用NSZone,也不能調用父類的dealloc。
MRC文件在ARC工程混合編譯時,需要在文件的Compiler Flags上添加什么參數
A -shared
B -fno-objc-arc
C -fobjc-arc
D -dynamic
參考答案:B
什么情況使用 weak 關鍵字,相比 assign 有什么不同?
什么情況使用weak關鍵字?
在 ARC 中,在有可能出現循環引用的時候,往往要通過讓其中一端使用 weak 來解決,比如: delegate 代理屬性
自身已經對它進行一次強引用,沒有必要再強引用一次,此時也會使用 weak,自定義 IBOutlet 控件屬性一般也使用 weak;當然,也可以使用strong。
weak與assign的不同?
weak 此特質表明該屬性定義了一種“非擁有關系” (nonowning relationship)。為這種屬性設置新值時,設置方法既不保留新值,也不釋放舊值。此特質同assign類似, 然而在屬性所指的對象遭到摧毀時,屬性值也會清空(nil out)。 而 assign 的“設置方法”只會執行針對“純量類型” (scalar type,例如 CGFloat 或 NSlnteger 等)的簡單賦值操作。
assigin 可以用非 OC 對象,而 weak 必須用于 OC 對象
調用對象的release 方法會銷毀對象嗎?
不會,調用對象的release 方法只是將對象的引用計數器-1,當對象的引用計數器為0的時候會調用了對象的dealloc 方法才能進行釋放對象的內存。
自動釋放池常見面試代碼
for (int i = 0; i < someLargeNumber; ++i)
{
NSString *string = @"Abc";
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@"%@",string);
}
問:以上代碼存在什么樣的問題?如果循環的次數非常大時,應該如何修改?
存在問題:問題處在每執行一次循環,就會有一個string加到當前runloop中的自動釋放池中,只有當自動釋放池被release的時候,自動釋放池中的標示了autorelease的這些數據所占用的內存空間才能被釋放掉。假設,當someLargeNumber大到一定程度時,內存空間將被耗盡而沒有被釋放掉,所以就出現了內存溢出的現象。
解決辦法1:如果i比較大,可以用@autoreleasepool {}解決,放在for循環外,循環結束后,銷毀創建的對象,解決占據棧區內存的問題
解決方法2:如果i玩命大,一次循環都會造成自動釋放池被填滿,自動釋放池放在for循環內,每次循環都將上一次創建的對象release
修改之后:
for(int i = 0; i<1000;i++){
NSAutoreleasePool * pool1 = [[NSAutoreleasePool alloc] init];
NSString string = @"Abc";
string = [string lowercaseString];
string = [string stringByAppendingString:@"xyz"];
NSLog(@"%@",string);
//釋放池
[pool1 drain]; }
objective-C對象的內存布局是怎樣的?
由于Objective-C中沒有多繼承,因此其內存布局還是很簡單的,就是:最前面有個isa指針,然后父類的實例變量存放在子類的成員變量之前
看下面的程序,第一個NSLog會輸出什么?這時str的retainCount是多少?第二個和第三個呢? 為什么?
NSMutableArray ary = [[NSMutableArray array] retain];
NSString *str = [NSString stringWithFormat:@"test"];
[str retain];
[aryaddObject:str];
NSLog(@”%@%d”,str,[str retainCount]);
[str retain];
[str release];
[str release];
NSLog(@”%@%d”,str,[str retainCount]);
[aryremoveAllObjects]
NSLog(@”%@%d”,str,[str retainCount]);
str的retainCount創建+1,retain+1,加入數組自動+1 3
retain+1,release-1,release-1 2
數組刪除所有對象,所有數組內的對象自動-1 1
回答person的retainCount值,并解釋為什么
Person * per = [[Person alloc] init]; 此時person 的retainCount的值是1 self.person = per;
在self.person 時,如果是assign,person的 retainCount的值不變,仍為1 若是:retain person的retainCount的值加1,變為2
若是:copy person的retainCount值不變,仍為1
什么時候需要在程序中創建內存池?
用戶自己創建的數據線程,則需要創建該線程的內存池
如果我們不創建內存池,是否有內存池提供給我們?
界面線程維護著自己的內存池,用戶自己創建的數據線程,則需要創建該線程的內存池
蘋果是如何實現autoreleasepool的?
autoreleasepool以一個隊列數組的形式實現,主要通過下列三個函數完成.
? objc_autoreleasepoolPush
? objc_autoreleasepoolPop
? objc_autorelease
看函數名就可以知道,對autorelease分別執行push、pop操作。銷毀對象時執行release操作。
objc使用什么機制管理對象內存?
通過引用計數器(retainCount)的機制來決定對象是否需要釋放。 每次runloop完成一個循環的時候,都會檢查對象的 retainCount,如果retainCount為0,說明該對象沒有地方需要繼續使用了,可以釋放掉了。
為什么要進行內存管理?
因為移動設備的內存極其有限,當一個程序所占內存達到一定值時, 系統會發出內存警告. 當程序達到更大的值時, 程序會閃退, 影響用戶體驗. 為了保證程序的運行流暢, 必須進行內存管理
內存管理的范圍?
管理所有繼承自NSObject的對象, 對基本數據類型無效.是因為對象和其他數據類型在系統中存儲的空間不一樣,其他局部變量主要存儲在棧區(因為基本數據類型占用的存儲空間是固定的,一般存放于棧區),而對象存儲于堆中,當代碼塊結束時,這個代碼塊所涉及到的所有局部變量會自動彈棧清空,指向對象的指針也會被回收,這時對象就沒有指針指向,但依然存在于堆內存中,造成內存泄露.
objc使用什么機制管理對象內存(或者內存管理方式有哪些)?(重點)
MRC(manual retain-release)手動內存管理
ARC(automatic reference counting)自動引用計數
Garbage collection (垃圾回收)。但是iOS不支持垃圾回收, ARC作為LLVM3.0編譯器的一項特性, 在iOS5.0 (Xcode4) 版本后推出的。
ARC的判斷準則, 只要沒有強指針指向對象, 對象就會被釋放.
iOS是如何管理內存的?
這個問題的話上一個問題也提到過,講下block的內存管理,ARC下的黃金法則就行。
這里說下swift里的內存管理:
delgate照樣weak修飾,閉包前面用[weak self],swift里的新東西,unowned,舉例,如果self在閉包被調用的時候可能為空,則用weak,反之亦然,如果為空時使用了unowned,程序會崩潰,類似訪問了懸掛指針,在oc中類似于unsafe_unretained,類似assign修飾了oc對象,對象被銷毀后,被unowned修飾的對象不會為空,但是unowned訪問速度更快,因為weak需要unwarp后才能使用
內存管理的原則
只要還有人在使用這個對象, 那么這個對象就不會被回收
只有你想使用這個對象, 那么就應該讓這個對象的引用計數器加1
當你不想使用這個對象時, 應該讓對象的引用計數器減1
誰創建, 就由誰來release
如果你通過alloc, new, copy 來創建一個對象, 當你不想用這個對象的時候就必須調用release 或者autorelease 讓引用計數器減1
不是你創建的就不用你負責 release
誰retain 誰release
只要你調用了retain ,無論這個對象如何生成, 都需要調用release
總結:
有加就應該有減, 曾讓某個計數器加1, 就應該讓其在最后減1
內存管理研究的對象:
野指針:指針變量沒有進行初始化或指向的空間已經被釋放。
使用野指針調用對象方法,會報異常,程序崩潰。
通常再調用完release方法后,把保存對象指針的地址清空,賦值為nil,找oc中沒有空指針異常,所以[nil retain]調用方法不會有異常。
內存泄露
如 Person * person = [Person new]; (對象提前賦值nil或者清空)在棧區的person已經被釋放, 而堆區new產生的對象還沒有釋放, 就會造成內存泄露
在MRC手動引用計數器模式下, 造成內存泄露的情況
沒有配對釋放,不符合內存管理原則
對象提前賦值nil或者清空,導致release不起作用。
僵尸對象 : 堆中已經被釋放的對象(retainCount = 0)
空指針 : 指針賦值為空,nil
如何判斷對象已經被銷毀
重寫dealloc方法,對象銷毀時,會調用,重寫時一定要[super dealloc]
retainCount = 0,使用retain能否復活對象
已經被釋放的對象無法復活
對象與對象之間存在的關系
繼承關系
組合關系(是一種強烈的包含關系)
依賴關系(對象作為方法參數傳遞)
對象的組合關系中,確保成員變量不被提前釋放?
重寫set方法,在set方法中,retain該對象。
成員變量的對象,在哪里配對釋放?
dealloc中釋放
對象組合關系中,內存泄露有哪幾種情況?
set方法沒有retain對象
沒有release舊對象
沒有判斷向set方法中傳入的是否為同一個對象
正確重寫set方法
判斷是否為同一對象
release舊對象
retain新對象
分別描述內存管理要點、autorelease、release、NSAutoreleasePool?并說明autorelease是什么時候被release的?簡述什么時候由你負責釋放對象,什么時候不由你釋放?[NSAutoreleasePool release]和[NSAutoreleasePool drain]有什么區別?
內存管理要點:Objective-C 使用引用計數機制(retainCount)來管理內存。
內存每被引用一次,該內存的引用計數+1,每被釋放一次引 用計數-1。
當引用計數 = 0 的時候,調用該對象的 dealloc 方法,來徹底從內存中刪除該對象。
alloc,allocWithZone,new(帶初始化)時:該對象引用計數 +1;
retain:手動為該對象引用計數 +1;
copy:對象引用計數 +1;//注意copy的OC數據類型是否有mutable,如有為深拷貝,新對象計數為1,如果沒有,為淺拷貝,計數+1
mutableCopy:生成一個新對象,新對象引用計數為 1;
release:手動為該對象引用計數 -1;
autorelease:把該對象放入自動釋放池,當自動釋放池釋放時,其內的對象引用計數 -1。
NSAutoreleasePool: NSAutoreleasePool是通過接收對象向它發送的autorelease消息,記錄該對象的release消息,當自動釋放池被銷毀時,會自動向池中的對象發送release消息。
autorelease 是在自動釋放池被銷毀,向池中的對象發送release
只能釋放自己擁有的對象。
區別是:在引用計數環境下(在不使用ARC情況下),兩者基本一樣,在GC(垃圾回收制)環境下,release 是一個no-op(無效操作),所以無論是不是GC都使用drain
面試中內存管理,release和autorelease的含義?這里尤其要強調下autorelease,它引申出自動釋放池,也能引申出Run loop!
自動釋放池是什么,如何工作 ?
什么是自動釋放池:用來存儲多個對象類型的指針變量
自動釋放池對池內對象的作用:存入池內的對象,當自動釋放池被銷毀時,會對池內對象全部做一次release操作
對象如何加入池中:調用對象的autorelease方法
自動釋放池能嵌套使用嗎:能
自動釋放池何時被銷毀 :簡單的看,autorelease的"}"執行完以后。而實際情況是Autorelease對象是在當前的runloop迭代結束時釋放的,而它能夠釋放的原因是系統在每個runloop迭代中都加入了自動釋放池Push和Pop
多次調用對象的autorelease方法會導致:野指針異常
自動釋放池的作用:將對象與自動釋放池建立關系,池子內調用autorelease,在自動釋放池銷毀時銷毀對象,延遲release銷毀時間
自動釋放池什么時候釋放?
通過Observer監聽RunLoop的狀態,一旦監聽到RunLoop即將進入睡眠等待狀態,就釋放自動釋放池(kCFRunLoopBeforeWaiting)
IPhone OS有沒有垃圾回收?autorelease 和垃圾回收制(gc)有什么關系?
iOS 中沒有垃圾回收。autorelease只是延遲釋放,gc是每隔一段時間詢問程序,看是否有無指針指向的對象,若有,就將它回收。他們兩者沒有什么關系。
ARC問題
什么是arc機制:自動引用計數.
系統判斷對象是否銷毀的依據:指向對象的強指針是否被銷毀
arc的本質:對retainCount計算,創建+1 清空指針 - 1 或者到達autoreleasepool的大括號-1
arc目的:不需要程序員關心retain和release操作.
如何解決arc機制下類的相互引用:.h文件中使用@class關鍵字聲明一個類,兩端不能都用強指針,一端用strong一端用weak
ARC通過什么方式幫助開發者管理內存?
ARC相對于MRC,不是在編譯時添加retain/release/autorelease這么簡單。應該是編譯期和運行期兩部分共同幫助開發者管理內存。
在編譯期,ARC用的是更底層的C接口實現的retain/release/autorelease,這樣做性能更好,也是為什么不能在ARC環境下手動retain/release/autorelease,同時對同一上下文的同一對象的成對retain/release操作進行優化(即忽略掉不必要的操作)
ARC也包含運行期組件,這個地方做的優化比較復雜,但也不能被忽略,手動去做未必優化得好,因此直接交給編譯器來優化,相信蘋果吧!
開發項目時你是怎么檢查內存泄露
靜態分析 analyze
instruments工具里面有個leak 可以動態分析
如果在block中多次使用 weakSelf的話,可以在block中先使用strongSelf,防止block執行時weakSelf被意外釋放
對于非ARC,將 __weak 改用為 __block 即可
麻煩你設計個簡單的圖片內存緩存器(移除策略是一定要說的)
內存緩存是個通用話題,每個平臺都會涉及到。cache算法會影響到整個app的表現。候選人最好能談下自己都了解哪些cache策略及各自的特點。
常見的有FIFO,LRU,LFU等等。由于NSCache的緩存策略不透明,一些app開發者會選擇自己做一套cache機制,其實并不難。
FIFO : 新訪問的數據插入FIFO隊列尾部,數據在FIFO隊列中順序移動;淘汰FIFO隊列頭部的數據;
LRU : 新數據插入到鏈表頭部;每當緩存數據命中,則將數據移到鏈表頭部;當鏈表滿的時候,將鏈表尾部的數據丟棄;
LFU : 新加入數據插入到隊列尾部(因為引用計數為1);隊列中的數據被訪問后,引用計數增加,隊列重新排序;當需要淘汰數據時,將已經排序的列表最后的數據塊刪除;
常見的出現內存循環引用的場景有哪些?
定時器(NSTimer):NSTimer經常會被作為某個類的成員變量,而NSTimer初始化時要指定self為target,容易造成循環引用(self->timer->self)。 另外,若timer一直處于validate的狀態,則其引用計數將始終大于0,因此在不再使用定時器以后,應該先調用invalidate方法
block的使用:block在copy時都會對block內部用到的對象進行強引用(ARC)或者retainCount增1(非ARC)。在ARC與非ARC環境下對block使用不當都會引起循環引用問題, 一般表現為,某個類將block作為自己的屬性變量,然后該類在block的方法體里面又使用了該類本身,簡單說就是self.someBlock =Type var{[self dosomething];或者self.otherVar = XXX;或者_otherVar = …};出現循環的原因是:self->block->self或者self->block->_ivar(成員變量)
代理(delegate):在委托問題上出現循環引用問題已經是老生常談了,規避該問題的殺手锏也是簡單到哭,一字訣:聲明delegate時請用assign(MRC)或者weak(ARC),千萬別手賤玩一下retain或者strong,畢竟這基本逃不掉循環引用了!
對象添加到通知中心中,當通知中心發通知時,這個對象卻已經被釋放了,可能會出現什么問題?
其實這種只是考查對通知的簡單應用。通知是多對多的關系,主要使用場景是跨模塊傳值。當某對象加入到通知中心后,若在對象被銷毀前不將該對象從通知中心中移除,當發送通知時,就會造成崩潰。這是很常見的。所以,在添加到通知中心后,一定要在釋放前移除。
ARC下不顯式指定任何屬性關鍵字時,默認的關鍵字都有哪些?
對于基本數據類型默認關鍵字是:atomic,readwrite,assign
對于普通的Objective-C對象:atomic,readwrite,strong
寫一個便利構造器
+(id)Person {
Person *person=[Person alloc]init];
return [person autorelease]; 備注:ARC時不用 autorelease
}
寫出下面程序段的輸出結果
NSDictionary *dict = [NSDictionary dictionaryWithObject:@"a string value" forKey:@"akey"]; NSLog(@"%@", [dict objectForKey:@"akey"]);
[dict release];
打印輸出 a string value,然后崩潰----原因:便利構造器創建的對象,之后的release,會造成過度釋放
請寫出以下代碼的執行結果
NSString * name = [ [ NSString alloc] init ];
name = @”Habb”;
[ name release];
打印輸出結果是: Habb,在[name release]前后打印均有輸出結果 ---會造成內存泄露---原先指向的區域變成了野指針,之后的釋放,不能釋放之前創建的區域
寫出方法獲取ios內存使用情況?
iOS是如何管理內存的?
我相信很多人的回答是內存管理的黃金法則,其實如果我是面試官,我想要的答案不是這樣的。我希望的回答是工作中如何處理內存管理的。
參考答案:
Block內存管理:由于使用block很容易造成循環引用,因此一定要小心內存管理問題。最好在基類controller下重寫dealloc,加一句打印日志,表示類可以得到釋放。如果出現無打印信息,說明這個類一直得不到釋放,表明很有可能是使用block的地方出現循環引用了。對于block中需要引用外部controller的屬性或者成員變量時,一定要使用弱引用,特別是成員變量像_testId這樣的,很多人都沒有使用弱引用,導致內存得不到釋放。
對于普通所創建的對象,因為現在都是ARC項目,所以記住內存管理的黃金法則就可以解決。
很多內置的類,如tableview的delegate的屬性是assign不是retain?
tableview的代理一般都是它所屬的控制器,控制器會對它內部的view進行一次retain操作,而tableview對代理控制器也進行一次retain操作,就會出現循環引用問題。
.主要是你在iOS里使用 while (true) {} 無線循環時, iOS ARC默認認為你這個方法永遠沒有執行完,所以不會去主動釋放你方法里的對象,這一點和JAVA不一樣, 所以很多JAVA開發者轉iOS后習慣性的使用while(true){} 導致項目里存在這種內存隱患,導致內存無限增加
----01>添加@autorelesepool{
}
----02>使用nstime做無限循環,arc下會自動釋放內存
2.ARC模式下oc對象與非oc對象之間的橋接 非oc對象需要手動釋放。
__bridge:不涉及對象所有關系改變
__bridge_transfer:給予 ARC 所有權
__bridge_retained:解除 ARC 所有權
3.如果在WebView載入完成之前關閉畫面的話,畫面關閉后,ViewController也釋放了。但由于WebView正在載入頁面,而不會馬上被釋放,等到頁面載入完畢后,回調delegate(ViewController)中的方法,由于此時ViewController已經被釋放,所以會出錯。(message sent to deallocated instance)
解決辦法是在dealloc中把WebView的delegate釋放。
4.ARC模式下,自動回收只針對Objective-C對象有效,對于使用create,copy,retain等生成的Core Foundation對象還是需要我們手動進行釋放的,CFRelease()