《Objective-C高級編程》自動引用計數 閱讀筆記 item1(內存管理/引用計數)

《Objective-C高級編程》自動引用計數 閱讀筆記系列

《Objective-C高級編程》自動引用計數 閱讀筆記 item1(內存管理/引用計數)
《Objective-C高級編程》自動引用計數 閱讀筆記 item2(ARC規則及其實現)

前言

放假前從圖書館借了幾本技術書,Objective-C高級編程是其中的一本,在豆瓣里評價挺高的,有8.2。雖然現在iOS的絕大多數項目基本上都是ARC的了,但是深入了解下蘋果的引用計數式內存管理的思考方式總是好的。認真地看完了第一章自動引用計數,像這種深入底層、源代碼講解知識點的方式很棒,但是這排版真的很糟糕,經常得對一個知識點看個好幾遍才弄懂在講什么。另外,中文博大精深,要是在閱讀的過程中,遇到長句時,如果沒正確斷好語句,可能句子的意思會千差萬別(ps:古時候,文言文似乎是沒有句號等符號的,感謝發明符號的人,要不然現在的中文更難理解千萬倍)。本著把書讀薄的精神,試著做了下閱讀筆記,大多數都是摘抄自書籍。

1.1 自動引用計數(ARC,Automatic Reference Counting)

概念:指內存管理中對引用采取自動計數的技術。
關鍵點:在LLVM編譯器中設置ARC為有效狀態,就無需再次鍵入retain或者是release代碼。

1.2 內存管理/引用計數

1.2.1 通過辦公室照明形象地解釋內存管理,

Snip20160126_3.png

表1 對辦公室照明設備所做的動作和對Objective-C的對象所做的動作

對辦公室照明設備所做的動作 對Objective-C的對象所做的動作
開燈 生成對象
需要照明 持有對象
不需要照明 釋放對象
關燈 廢棄對象

由此,可以推出引用計數的內存管理

Snip20160126_4.png

1.2.2 內存管理的思考方式

  • 自己生成的對象,自己所持有
  • 非自己生成的對象,自己也能持有
  • 不再需要自己持有的對象時釋放
  • 非自己持有的對象無法釋放

表2 對象操作與Objective-C方法的對應

對象操作 Objective-C方法
生成并持有對象 alloc/new/copy/mutableCopy等方法
持有對象 retain方法
釋放對象 release方法
廢棄對象 dealloc方法

有關Objective-C內存管理的方法并不包含在Objective-C語言中,而是在包含在Cocoa框架中。如下:

Snip20160126_5.png

*** 自己生成的對象,自己所持有 ***
使用以下名稱開頭的方法意味著自己生成的對象只有自己持有:

  • alloc
  • new
  • copy
  • mutableCopy

注意:下列幾個方法,并不是同一類別的方法:

  • allocate
  • newer
  • copying
  • mutableCopyed

*** 非自己生成的對象,自己也能持有 ***
用alloc/new/copy/mutableCoy以外的方法取得對象,因為非自己生成并持有,所以自己不是該對象的持有者。但是通過retain方法,非自己生成的對象跟用alloc/new/copy/mutableCoy方法生成并持有的對象一樣,成為了自己所持有的。

*** 不再需要自己持有的對象時釋放 ***

  • 自己持有的對象,一旦不再需要,持有者有義務釋放該對象,務必使用release方法釋放。
  • 使用autorelease方法,可以使取得的對象存在,但自己不持有對象
  • 用來取得誰都不持有的對象的方法名不能以alloc/new/copy/mutableCopy開頭
  • 通過retain方法也能將調用autorelease方法取得的對象變為自己持有

注意:autorelease的功能

Snip20160126_6.png

*** 非自己持有的對象無法釋放 ***
釋放非自己持有的對象會造成程序崩潰。
相關案例:

  • 自己生成并持有對象后,在釋放完不再需要的對象之后再次釋放
  • 在“取得的對象存在,但自己不持有對象”時釋放

1.2.3 alloc/retain/release/dealloc實現

借助開源軟件GNUstep的源代碼中alloc/retain/release/dealloc的實現來理解蘋果的Cocoa實現。
總結如下:

  • 在Objective-C的對象中存在引用計數這一整數值
  • 調用alloc或是retain方法后,引用計數值加1
  • 調用release方法后,引用計數值減1
  • 引用計數值為0時,調用dealloc方法廢棄對象

1.2.4 蘋果的實現

由于NSObject類的源代碼沒有公開,利用Xcode的調試器(lldb)和iOS大概追溯內存管理和引用計數的實現。
蘋果的實現大概就是采用散列表(引用計數表)來管理引用計數。
如圖:

Snip20160126_7.png

GNUstep將引用計數保存在對象占用內存塊頭部的變量中,而蘋果的實現,則是保存在引用計數表的記錄中。
通過內存卡頭部管理引用計數的好處如下:

  • 少量代碼即可完成
  • 能夠統一管理引用計數用內存塊與對象用內存塊

通過引用計數表管理引用計數的好處如下:

  • 對象用內存塊的分配無需考慮內存塊頭部
  • 引用計數表各記錄中存有內存塊地址,可從各個記錄中追溯到各對象的內存塊

*** 關于蘋果的實現的第2條特性在調試時有著舉足輕重的作用:***

  • 即使出現故障導致對象占用的內存塊損壞,但只要引用計數表沒有被破壞,就能夠確認各內存塊的位置,如下:


    Snip20160126_8.png
  • 在利用工具檢測內存泄露時,引用計數表的各記錄也有助于檢測各對象的持有者是否存在

1.2.5 autorelease

autorelease的作用:

當超出對象實例的作用域(相當于變量作用域)時,對象實例的release實例方法被調用。

autorelease的具體使用方法如下:

  1. 生成并持有NSAutoreleasePool對象
  2. 調用已分配對象的autorelease實例方法
  3. 廢棄NSAutoreleasePool對象
Snip20160126_10.png

關鍵知識點:

  • 在大量產生autorelease的對象時,只要不廢棄NSAutoreleasePool對象,那么生成的對象就不能被釋放,因此有時產生內存不足的現象。例如,讀入大量圖像的同時改變其尺寸。在這類情況下,有必要在適當的地方生成、持有或廢棄NSAutoreleasePool對象。
Snip20160126_11.png
  • Cocoa框架中有很多類方法用于返回autorelease的對象。例如NSMutableArray類的arrayWithCapacity類方法

1.2.6 autorelease的實現

為了理解autorelease的實現,同樣需要查看GNUstep的源代碼。

關鍵知識點:

  • autorelease實例方法的本質就是調用NSAutoreleasePool對象的addObject類方法
  • addObject類方法調用正在使用的NSAutoreleasePool對象的addObject實例方法
  • 如果嵌套生成或持有的NSAutoreleasePool對象,理所當然會使用最內側的對象
  • 如果調用NSObject類的autorelease實例方法,該對象將被追加到正在使用的NSAutoreleasePool對象中的數組中
  • drain實例方法在廢棄autorelease對象數組之前,會先對數組中的所有對象調用release實例方法

1.2.7 蘋果的實現

通過objc4庫的runtime/objc-arr.mm可以確認蘋果的實現。

關鍵方法:

  • void *objc_autoreleasePoolPush(void){}
  • void objc_autoreleasePoolPop(void *ctxt){}
  • void *objc_autorelease(id obj){}

另外,通過NSAutoreleasePool類中的調用用非公開類方法showPools(該類方法只能在iOS中使用)來確認已被autorelease的對象的狀況。調試輸出用非公開函數_objc_autoreleasePoolPrint()。該函數在檢查某對象是否被自動release時非常有用。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容