內存管理
ARC處理原理
ARC是Objective-C編譯器的特性,而不是運行時特性或者垃圾回收機制,ARC所作的只不過是在代碼編譯時爲你自動在合適的位置插入release或autorelease,只要沒有強指針指向對象,對象就會被釋放。前端
前端編譯器會爲「擁有的」每個對象插入相應的release語句。若是對象的全部權修飾符是__strong,那麼它就是被擁有的。若是在某個方法內建立了一個對象,前端編譯器會在方法末尾自動插入release語句以銷燬它。而類擁有的對象(實例變量/屬性)會在dealloc方法內被釋放。事實上,你並不須要寫dealloc方法或調用父類的dealloc方法,ARC會自動幫你完成一切。此外,由編譯器生成的代碼甚至會比你本身寫的release語句的性能還要好,由於編輯器能夠做出一些假設。在ARC中,沒有類能夠覆蓋release方法,也沒有調用它的必要。ARC會經過直接使用objc_release來優化調用過程。而對於retain也是一樣的方法。ARC會調用objc_retain來取代保留消息。ios
雖然前端編譯器聽起來很厲害的樣子,但代碼中有時仍會出現幾個對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);
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後才能使用swift
內存管理的原則
- 只要還有人在使用這個對象, 那麼這個對象就不會被回收
- 只有你想使用這個對象, 那麼就應該讓這個對象的引用計數器加1
- 當你不想使用這個對象時, 應該讓對象的引用計數器減1
- 誰建立, 就由誰來release
- 若是你經過alloc, new, copy 來建立一個對象, 當你不想用這個對象的時候就必須調用release 或者autorelease 讓引用計數器減1
- 不是你建立的就不用你負責 release
- 誰retain 誰release
- 只要你調用了retain ,不管這個對象如何生成, 都須要調用release
- 總結:
有加就應該有減, 曾讓某個計數器加1, 就應該讓其在最後減1
內存管理研究的對象:
如何判斷對象已經被銷燬
- 重寫dealloc方法,對象銷燬時,會調用,重寫時必定要[super dealloc]
retainCount = 0,使用retain可否復活對象
對象與對象之間存在的關係
- 繼承關係
- 組合關係(是一種強烈的包含關係)
- 依賴關係(對象做爲方法參數傳遞)
對象的組合關係中,確保成員變量不被提早釋放?
- 重寫set方法,在set方法中,retain該對象。
成員變量的對象,在哪裏配對釋放?
對象組合關係中,內存泄露有哪幾種狀況?
- set方法沒有retain對象
- 沒有release舊對象
- 沒有判斷向set方法中傳入的是否爲同一個對象
正確重寫set方法
- 判斷是否爲同一對象
- release舊對象
- retain新對象
分別描述內存管理要點、autorelease、release、NSAutoreleasePool?並說明autorelease是何時被release的?簡述何時由你負責釋放對象,何時不禁你釋放?[NSAutoreleasePool release]和[NSAutoreleasePool drain]有什麼區別?
-
內存管理要點:Objective-C 使用引用計數機制(retainCount)來管理內存。app
-
內存每被引用一次,該內存的引用計數+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操做,就會出現循環引用問題。
文章若有問題,請留言,我將及時更正。