Objective-C高級編程讀書筆記

自動引用計數

ARC

自動引用計數 ARC :是指內存管理中對引用計數采取自動計數的計數。

蘋果文檔

ARC 是讓編譯器來進行內存管理。設置了 ARC 為有效狀態,就無需再次鍵入 retain 或者 release 代碼。降低了程序崩潰,內存泄漏等風險的風險的同時,很大程度減少了開發程序的工作量。編譯器完全清楚目標對象,并能立刻釋放那些不再使用的對象。

內存管理

  • 自己生成的對象,自己所持有
  • 非自己生成的對象,自己也可以持有
  • 不再需要自己持有的對象時釋放
  • 非自己持有的對象無法釋放
  • autorelease 對象
對象操作 Objective-C 方法
生成并持有對象 alloc/new/copy/mutableCopy等方法
持有對象 retain
釋放對象 release
廢棄對象 dealloc

自己生成的對象,自己所持有

下列名稱開頭的方法名意味著自己生成的對象自己所持有。

  • alloc
  • new
  • copy
  • mutableCopy

實例代碼

id obj = [NSObject new];

非自己生成的對象,自己也可以持有

用 alloc/new/copy/mutableCopy 以外的方法取得的對象,因為非自己生成并持有,所以不是該對象的持有者。但是我們可以持有它

實例代碼:

// 取得非自己生成的對象
id obj = [NSMutableArray array];

// 持有非自己生成的對象
[obj retain];

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

自己持有的對象,一旦不在需要,持有者有義務釋放該對象,釋放使用 release。
實例代碼:

// 自己生成并持有對象
id obj = [[NSObject alloc] init];

// 釋放對象
[obj release];

同理,用 alloc 方法由自己生成并持有的對象通過 release 方法就釋放了。非自己生成而持有的對象,若用 retain 方法變為自己持有,也可以使用 release 方法釋放。

// 取得非自己生成的對象
id obj = [NSMutableArray array];

// 持有對象
[obj retain];

// 釋放對象
[obj release];

非自己持有的對象無法釋放

釋放非自己持有的對象會造成崩潰
實例代碼:

// 自己生成并持有
id obj = [NSObject new];
// 釋放持有對象
[obj release];
// 釋放非自己持有的對象,因為之前已經釋放了。crash !!!
[obj release];

autorelease 對象

autorelease 對象可以達到對象存在但是自己又不持有的效果。如 [NSMutableArray array]

實現方案:

- (NSMutableArray *) array
{
      // 創建并持有對象
      id obj = [NSObject new];
      // 取得對象存在,但是自己不持有
     [obj autorelease];
     return obj;
}

autorelease 對象會被 autoreleasepool 所持有,在 runloop 休眠的時候會釋放所持有的對象。

GUNstep 的計數實現

將引用計數存在對象占用的內存塊頭部的變量中。

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

蘋果的實現

蘋果使用了散列表來管理引用計數。
對比:

GUNstep

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

Apple

  • 對象用內存快的分配無需考慮內存塊頭部。
  • 引用計數表各記錄中存在內存塊地址,可以從各個記錄追溯到各對象內存塊。利于調試。

autorelease

autorelease 對象可以達到對象存在但是自己又不持有的效果。autorelease 對象離開作用域都將調用 release 實例方法。
具體使用(MRC):

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

實例代碼:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [NSObject new];
[obj autorelease];
[pool drain];

ARC 規則

所有權修飾符

  • __strong 修飾符
  • __weak 修飾符
  • __unsafe_unretained 修飾符
  • __autorelease 修飾符

__strong 修飾符

__strong 是默認修飾符
__strong 修飾符表示對對象的強引用。持有強引用的變量在超出其作用域時被廢棄,隨著強引用失效,引用的對象會隨之釋放。

  • 如果是自己生成的對象正常持有。
  • 如果不是自己生成的對象,會自動加 retain 來持有。

__weak 修飾符

__strong 修飾符會造成循環引用問題,__weak 修飾符可以避免循環引用。__weak 不持有對象,在對象被釋放的時候會自動設置為 nil。
__weak 修飾符只能用于 iOS 5 以上以及 OS X Lion 以上版本,其他版本需要使用 __unsafe_unretained 來替代。

__unsafe_unretained 修飾符

__unsafe_unretained 修飾符是不安全的所有權修飾符。盡管 ARC 是由編譯器管理的,但是 __unsafe_unretained 修飾符修飾的變量不屬于編譯器的內存管理對象。
和 __weak 不持有對象一樣。釋放了不會被設置為 nil,會出現野指針的情況。

__autorelease 修飾符

在 ARC 下不能顯示的調用 autorelease 方法,那么需要使用 __autorelease 修飾。
實例代碼:

// MRC 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
id obj = [NSObject new];
[obj autorelease];
[pool drain];

// ARC
@autoreleasepool{
  id __autorelease obj = [[NSObject alloc] init];
}

知識點:

  1. 訪問 weak 變量是必須訪問注冊到 autorelease 的對象,因為 weak 修飾符只持有對象的弱引用,而在訪問過程中,該對象可能被廢棄。如果把要訪問的對象注冊到 autoreleasepool 中,那么在 runloop 周期內都不會給釋放。
  2. id 的指針和對象的指針(如 NSError **)沒有顯示的修飾符那么默認是 __autorelease.

ARC 下編寫代碼規則

  • 不能使用 retain / release / retainCount /autorelease。
  • 不能使用 NSAllocateObject / NSDeallocateObject。
  • 必須遵守內存管理的方法命名規則。
  • 不要顯式的調用 delloc。
  • 使用 @autoreleasepool 塊來替代 NSAutoreleasePool。
  • 不能使用區域(NSZone)
  • 對象型變量不能作為 C 語言結構體的成員。
  • 顯示轉換 id 和 void *。

Toll-Free Bridge

Core Foundation 對象和 Objective-C 對象沒有區別,不同之處只是由哪個框架生成對象, Core Foundation 對象可以轉換成 Objective-C 對象來使用,轉換過程稱之為 Toll-Free Bridge。有以下幾個關鍵字

  • __bridge :只做類型轉換,但是不修改對象(內存)管理權
  • __bridge_retained:將Objective-C的對象轉換為Core Foundation的對象,同時將對象(內存)的管理權交給我們,后續需要使用CFRelease或者相關方法來釋放對象。
  • __bridge_transfer:將Core Foundation的對象轉換為Objective-C的對象,同時將對象(內存)的管理權交給ARC。

ARC 實現

strong 修飾符

編譯器來管理,2 種情況

  1. alloc/new/copy/mutableCopy等方法生成的對象,會自動插入 release方法。
  2. 非情況 1 方法生成的對象,如 [NSMutableArray array] ,會分別插入objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue方法。

objc_autoreleaseReturnValueobjc_retainAutoreleasedReturnValue是成對出現的。在對象返回處調用objc_autoreleaseReturnValue,并且強引用的時候緊接著會調用objc_retainAutoreleasedReturnValue
系統會對這個其中賦值過程進行優化。

過程圖解.png

weak 修飾符

weak 對象流程

  1. 初始化 weak:objc_initWeak
  2. 改變 weak 值:objc_storeWeak
  3. 釋放 weak 值:objc_destroyWeak

Blocks

什么是 Blocks

Blocks 是 C 語言的擴充功能:帶有局部變量的匿名函數。
其他語言中的 Blocks 的名稱

語言 名稱
C Blocks
Smalltalk Blocks
Ruby Blocks
LISP Lambda
Python Lambda
C++ Lambda

語法

^ 返回值類型 參數列表{
  表達式
}

Blocks 變量

語法(用在屬性,變量處)

返回值類型 (^ 變量名)參數列表

截獲自動變量

  • 普通變量獲取值得內容
  • 指針變量強引用指針對象

__block

截獲的自動變量不能子啊 Blocks 塊內被修改,需要加 __block 修飾符。

Blocks 的實現

一個 Blocks 被編譯后會生成這么幾個結構體

  • __block_impx:包含了 isa,標志位,保留字段,執行的函數指針。
  • __xxx_block_desc_x:包含了保留字段,block 的大小。
  • xxx_block_impx_xxx:包含了上面 2 個成員。

x 表示由編譯器決定的名字。

截獲自動變量的原理

截獲的變量,會在__block_impx 中生成相應的成員變量。普通變量值是拷貝值,指針則是強引用指針。

__block 修飾符

當用 __block 修飾符的時候,會多生成__Block_byref_val_x,包含了被捕獲的值,isa,forwarding 指針等字段。
之所以可以在 block 內部修改 __block 變量因為生成這個結構體來包裝了一層,通過這個結構體達到修改外部變量的目的。

forwarding 指針

當一個棧 block 執行出了作用域的時候,其捕獲的 __block 變量也會出作用域,會出現野指針,如果把 __block 拷貝到堆上,這個時候這個 __block 變量也應該一同拷貝,需要有一個機制在被拷貝之后也會引用到系統的堆變量上。forwarding 指針就是做這個的,forwarding 在未被拷貝的時候始終指向自己,在拷貝之后 forwarding 指針會指向堆變量。防止了野指針的問題,也解決了拷貝之后的引用問題。

循環引用

在使用 block 由于捕獲變量都是強引用的,所以需要注意循環引用。可以使用 weak 變量來避免循環引用。

更詳細的內容可以看我另外一篇Block 博客

未完待續...

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,321評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,559評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,442評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,835評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,581評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,922評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,931評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,096評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,639評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,374評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,591評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,104評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,789評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,196評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,524評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,322評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,554評論 2 379

推薦閱讀更多精彩內容