iOS 內存管理

ARC

ARC是 Object-C 編譯特性, 不是運行時特性也不是垃圾回收機制, ARC 所做的只是在代碼編譯自動在合適的地方插入 release 或 autorelese, 只要沒有強指針指向對象, 對象就會被釋放

  • 前端編譯器
    前端編譯器會為"擁有的"每一個對象插入相應的release語句. 如果對象的修飾符是 __strong, 那么它就是被擁有的. 如果再某個方法內創(chuàng)建了一個對象, 前端編譯器會為他的末尾自動插入 release 語句可以銷毀它. 而類擁有的對象會在 dealloc 方法內被釋放. 事實上, 你并不需要寫 dealloc 方法或者調用父類的方法, ARC 會自動幫你完成一切. 此外, ARC 生成的代碼甚至會比你寫的 release 語句的性能還要好, 因為編譯器可以做出一些假設. 在 ARC 中, 沒有類可以覆蓋 release方法, 也沒有調用它的必要. ARC 會通過直接使用objc_release 來優(yōu)化調用過程. 而對于 retain 也是同樣的方法. ARC 會調用 objc_retain 來取代保留消息.
  • ARC 優(yōu)化器
    雖然前端編譯器聽起來很流弊的樣子, 但是代碼中有事還是會出現(xiàn)對 retain 和 release 的重復調用. ARC 優(yōu)化器負責移除多余的 retain 和release 語句. 確保生成的代碼運行速度高于手動引用計數的代碼

測試, 下面關于 Objective-C 內存管理的錯誤描述是

A. 當使用 ARC 來管理內存時, 代碼中不可以出現(xiàn) autorelease
B. autoreLeasePool 在drain 的時候會釋放在其中分配的對象
C. 當使用 ARC 來管理內存的時候, 在線程中大量分配對象而不用 autoreleasepool 則可能會造成內存泄漏
D. 在使用 ARC 的項目中不能使用 NSZone

  • 參考答案: A
  • 理由: ARC 只是在大多時候編譯自動為我們添加上內存管理的代碼, 只是我們的源代碼看不到. 但是在編譯的時候會添加相關內存管理代碼. 對于自動釋放池, 在 drain 時會將自動釋放池中所有對象的應用計數減一, 若引用技術為 0, 則會自動釋放掉其內存. 如果在線程中需要大量分配內存, 我們理應添加自動釋放池, 以防內存泄漏. 比如在 for 循環(huán)中要分配大量的內存處理數據. 那么我們應該在 for 循環(huán)內添加自動釋放池, 在每個循環(huán)后釋放掉, 防止內存泄漏. 在 ARC 項目中, 自然不能手動使用 NSZone, 也不能調用父類的dealloc

MRC文件在ARC工程混編時, 需要在文件的 Compiler Flages 上添加什么參數

A. -shared B. -fno-objc-arc C. -fobjc-arc D. -dynamic
參考答案: B

什么情況使用 weak 關鍵字, 相比 assign 有什么不同

什么情況下使用 weak 關鍵字

  • 在 ARC 中, 在有可能出現(xiàn)循環(huán)引用的時候, 玩玩要讓其中一端使用 weak 來解決, 比如: delegate 代理屬性, blcok 里面的 self.
  • 自身已經對它進行一次強引用, 沒有必要再強引用一次, 此時也會使用 weak, 自定義 IBOutlet 控件一般也使用 weak: 當然, 也可以使用 strong.
    weak 和 assign 的不同
  • weak 此特質表明該屬性定義了一種"非擁有關系", 為這種屬性設置新值時, 設置方法既不保留新值, 也不釋放就只. 這個特性和 assign 類似, 然后在屬性所指的對象遭到摧毀時, 屬性值也會清空 (nil out). 而 assign 的 "設置方法" 只會執(zhí)行對 "純量類型" (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);
}

有問題的大兄弟, 每執(zhí)行一次循環(huán), 就會有一個 string 加到當前的 runloop 中的自動釋放池中, 只有當自動釋放池被 release 的時候, 自動釋放池中的 標示了 autorelease 的這些數據所占用的內存空間才能被釋放掉. 假設, 當 someLargeNumber 大到一定的成都的時候, 內存空間唄消耗干凈又沒有被釋放掉, 就會出現(xiàn)內存溢出.

  • 解決辦法 1 :如果 i 比較大, 可以用 @autoreleaseppol {} 解決, 放在 for 循環(huán)外, 循環(huán)結束后, 銷毀創(chuàng)建的對象, 解決占據棧區(qū)內存的問題
  • 解決方法 2 : 如果 i 不要臉的大, 一次訓話你都會造成自動釋放池被填滿, 自動釋放池放在 for 循環(huán)里面, 每次循環(huán)的時候都讓上一次創(chuàng)建的對象 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, 變?yōu)?2
    • copy 修飾, 值不變, 還為1

什么時候需要在程序中創(chuàng)建內存池

  • 用戶自己創(chuàng)建的數據線程, 炫耀創(chuàng)建改線程的內存池

我們不創(chuàng)建內存池, 時候有內存池提供給我們.

  • 界面線程維護自己的內存池, 用戶自己創(chuàng)建的數據線程, 則需要創(chuàng)建改線程的內存.

蘋果怎么實現(xiàn) autoreleasepool的

autoreleasepool 以一個隊列數組的形式實現(xiàn), 主要通過以下三個函數完成,

  • objc_autoreleasepoolPush
  • objc_autoreleasepoolPop
  • objc_autorelease
    根據函數名可知道, 對 autorelease 分別執(zhí)行 push, pop 操作. 銷毀對象時執(zhí)行 release 操作.

objc使用什么機子管理對象內存

  • 通過引用計數器的機子來決定對象是否需要釋放. 每次 runloop 完成一個循環(huán)的時候, 都會檢查對象的 retainCount, 如果 retainCount, 如果retainCount 為0, 說明該對象沒有地方需要繼續(xù)使用了, 可以釋放掉

為什么要進行內存管理

  • 因為移動設備的內存及其有限, 當一個程序說占內存達到一定數值的時候, 系統(tǒng)會發(fā)出內存警告, 當程序達到更大的值的時候, 程序會出現(xiàn)閃退, 影響用戶體驗. 為了保證程序的運行流暢, 必須進行內存管理.
  • 內存管理的范疇
  • 管理所有繼承自 NSObject 的對象, 對基本數據類型無效, 是因為對象和其他數據類型在系統(tǒng)中儲存的控件是不一樣的, 其他局部變量主要存儲在棧區(qū)(因為基本類型占用的存儲空間是固定的, 一般存放在棧區(qū)), 而對象存儲在堆中, 當代碼塊結束的時候, 這個代碼塊會設計到所有局部變量會自動彈棧清空, 指向對象的指針也會被回收, 這時對象就沒有指針指向, 但依然存在于堆內存中, 造成內存泄漏.

objc 使用什么機子管理對象內存(或者內存管理方式有哪些)

  • MRC (manual retain-release)手動內存管理
  • ARC (automatic reference counting) 自動引用計數
  • Garbage collection (垃圾回收). 但是 iOS 不支持垃圾回收, ARC 作為 LLVM 3.0 編譯器的一項特性, 在 iOS 5.0 (Xcode-4)版本后推出的.
  • ARC 的判斷準則, 只要沒有強指針指向對象, 對象就會被釋放.

內存管理原則

  • 只要還有人在使用這個對象, 那么這個對象就不會被回收
  • 只有你想使用這個對象, 那么就應該讓這個對象的引用計數器加1
  • 當你不想使用這個對象時, 應該讓對象的引用計數器減1
  • 誰創(chuàng)建, 就由誰來release
    • 如果你通過alloc, new, copy 來創(chuàng)建一個對象, 當你不想用這個對象的時候就必須調用release 或者autorelease 讓引用計數器減1
    • 不是你創(chuàng)建的就不用你負責 release
      誰retain 誰release
  • 只要你調用了retain ,無論這個對象如何生成, 都需要調用release
    總結:
  • 有加就應該有減, 曾讓某個計數器加1, 就應該讓其在最后減1

內存管理研究對象

  • 野指針: 指針變量沒有進行初始化或者指向的空間已經被釋放了
    • 使用野指針調用對象方法, 會報異常, 程序崩潰
    • 通常再調用玩 release 方法后, 把 保存對象指針的地址清空, 賦值為 nil, 找 oc 中沒有空指針異常, 所以[nil retain] 調用方法不會有異常
  • 內存泄漏
    • 如 Person *person = [Person new]; (對象提前賦值nil或者清空)在棧區(qū)的person已經被釋放, 而堆區(qū)new 產生的對象還沒有釋放, 就會造成內存泄漏.
    • 僵尸對象: 堆中已經被釋放的對象(retainCount = 0)
  • 空指針: 指針賦值為空, nil

未完待續(xù)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容