ARC
ARC是 Object-C 編譯特性, 不是運行時特性也不是垃圾回收機制, ARC 所做的只是在代碼編譯自動在合適的地方插入 release 或 autorelese, 只要沒有強指針指向對象, 對象就會被釋放
- 前端編譯器
前端編譯器會為"擁有的"每一個對象插入相應的release語句. 如果對象的修飾符是 __strong, 那么它就是被擁有的. 如果再某個方法內創建了一個對象, 前端編譯器會為他的末尾自動插入 release 語句可以銷毀它. 而類擁有的對象會在 dealloc 方法內被釋放. 事實上, 你并不需要寫 dealloc 方法或者調用父類的方法, ARC 會自動幫你完成一切. 此外, 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 Flages 上添加什么參數
A. -shared B. -fno-objc-arc C. -fobjc-arc D. -dynamic
參考答案: B
什么情況使用 weak 關鍵字, 相比 assign 有什么不同
什么情況下使用 weak 關鍵字
- 在 ARC 中, 在有可能出現循環引用的時候, 玩玩要讓其中一端使用 weak 來解決, 比如: delegate 代理屬性, blcok 里面的 self.
- 自身已經對它進行一次強引用, 沒有必要再強引用一次, 此時也會使用 weak, 自定義 IBOutlet 控件一般也使用 weak: 當然, 也可以使用 strong.
weak 和 assign 的不同 - weak 此特質表明該屬性定義了一種"非擁有關系", 為這種屬性設置新值時, 設置方法既不保留新值, 也不釋放就只. 這個特性和 assign 類似, 然后在屬性所指的對象遭到摧毀時, 屬性值也會清空 (nil out). 而 assign 的 "設置方法" 只會執行對 "純量類型" (scalar type, 例如 CGFloat 或 NSInteger 等) 的簡單賦值操作
- assign 可以用在非 OC 對象, weak 只能用在 OC 對象中.
調用對象的 release 方法會銷毀對象嗎
- no no no, 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 比較大, 可以用 @autoreleaseppol {} 解決, 放在 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];
}
Object-C 對象的內存布局是怎樣的
- Objective - C 中沒有多繼承, 因此內存布局還是挺簡單的, 也就是: 最前面是 isa 指針, 然后父類的實例變量存放在子類的成員變量之前.
看下面代碼, 第一個 NSLog 會輸出什么, 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]);
- 第一個是3, retain + 1, 加入數組自動 + 1, retainCount + 1, 總的為3
- 第二個是2, retain + 1, release - 1, release - 1. 值為2
- 最后一個1, 數組刪除 -1. 得到結果為1.
看看下面 person 的 retainCount 值
Person * per = [[Person alloc] init];
self.person = per;
- 第一句代碼 person 的 retainCount 的值是 1
- 在 self.person 時 , 分三種情況
- assign 修飾, 值不變, 還為 1
- retain 修飾, + 1, 變為 2
- copy 修飾, 值不變, 還為1
什么時候需要在程序中創建內存池
- 用戶自己創建的數據線程, 炫耀創建改線程的內存池
我們不創建內存池, 時候有內存池提供給我們.
- 界面線程維護自己的內存池, 用戶自己創建的數據線程, 則需要創建改線程的內存.
蘋果怎么實現 autoreleasepool的
autoreleasepool 以一個隊列數組的形式實現, 主要通過以下三個函數完成,
- objc_autoreleasepoolPush
- objc_autoreleasepoolPop
- objc_autorelease
根據函數名可知道, 對 autorelease 分別執行 push, pop 操作. 銷毀對象時執行 release 操作.
objc使用什么機子管理對象內存
- 通過引用計數器的機子來決定對象是否需要釋放. 每次 runloop 完成一個循環的時候, 都會檢查對象的 retainCount, 如果 retainCount, 如果retainCount 為0, 說明該對象沒有地方需要繼續使用了, 可以釋放掉
為什么要進行內存管理
- 因為移動設備的內存及其有限, 當一個程序說占內存達到一定數值的時候, 系統會發出內存警告, 當程序達到更大的值的時候, 程序會出現閃退, 影響用戶體驗. 為了保證程序的運行流暢, 必須進行內存管理.
- 內存管理的范疇
- 管理所有繼承自 NSObject 的對象, 對基本數據類型無效, 是因為對象和其他數據類型在系統中儲存的控件是不一樣的, 其他局部變量主要存儲在棧區(因為基本類型占用的存儲空間是固定的, 一般存放在棧區), 而對象存儲在堆中, 當代碼塊結束的時候, 這個代碼塊會設計到所有局部變量會自動彈棧清空, 指向對象的指針也會被回收, 這時對象就沒有指針指向, 但依然存在于堆內存中, 造成內存泄漏.
objc 使用什么機子管理對象內存(或者內存管理方式有哪些)
- MRC (manual retain-release)手動內存管理
- ARC (automatic reference counting) 自動引用計數
- Garbage collection (垃圾回收). 但是 iOS 不支持垃圾回收, ARC 作為 LLVM 3.0 編譯器的一項特性, 在 iOS 5.0 (Xcode-4)版本后推出的.
- ARC 的判斷準則, 只要沒有強指針指向對象, 對象就會被釋放.
內存管理原則
- 只要還有人在使用這個對象, 那么這個對象就不會被回收
- 只有你想使用這個對象, 那么就應該讓這個對象的引用計數器加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 產生的對象還沒有釋放, 就會造成內存泄漏.
- 僵尸對象: 堆中已經被釋放的對象(retainCount = 0)
- 空指針: 指針賦值為空, nil
未完待續