Objective-C高級編程:iOS與OS X多線程和內(nèi)存管理

工具:利用clang(LLVM編譯器)的命令:clang -rewrite-objc 源代碼文件名 將OC轉(zhuǎn)換成對應(yīng)的C++源代碼掰派。



另類總結(jié):
四類關(guān)鍵字alloc/new/copy/mutableCopy等 | retain | release | dealloc
四種所有權(quán)修飾符__strong | __weak | __unsafe_unretained | __autoreleasing
兩張散列表(引用計數(shù)表和weak表)+ 一個動態(tài)數(shù)組autoreleasepool)+NSRunLoop
屬性assign | copy | retain | strong | unsafe_unretained | weak
Toll-Free Bridge - 傳送門

思路串聯(lián):
MRC下酱床,內(nèi)存需要人工管理并齐,通過alloc等四類關(guān)鍵字(本質(zhì):calloc潮梯、free)結(jié)合引用計數(shù)表進行平挑,


參考:塊替代傳統(tǒng)回調(diào)函數(shù)或delegate的意義



1. 自動引用計數(shù)

在LLVM【編譯器】中設(shè)置ARC為有效狀態(tài)游添,就無需鍵入retain或release代碼了,編譯器將結(jié)合【OC運行時】基于引用計數(shù)自動進行內(nèi)存管理通熄。

引用計數(shù)/內(nèi)存管理

對照明設(shè)備所做的工作 對OC對象所做的動作
開燈 生成對象
需要照明 持有
不需要照明 釋放
關(guān)燈 廢棄
內(nèi)存管理的思考方式 對應(yīng)OC方法
自己生成的對象唆涝,自己所持有 alloc/new/copy/mutableCopy等
非自己生成的對象(比如[NSArray array]),自己也能持有 retain
1. 不再需要自己持有的對象時釋放<br />2. 無妨釋放非自己持有的對象(比如多次release) release
當對象不被任何其他對象持有時廢棄 dealloc

蘋果的實現(xiàn)

基于內(nèi)存塊地址-引用計數(shù)的哈希散列表進行管理
=> alloc/retain/retainCount/release/dealloc實現(xiàn)

alloc => 調(diào)用class_createInstance(calloc)分配內(nèi)存 => 設(shè)置isa指針和成員變量初始值(0) => 在引用計數(shù)表中添加紀錄唇辨,并將引用計數(shù)值置為1

case OPERATION_retain:
    CFBasicHashAddValue( table, obj );
    return obj;
case OPERATION_retainCount:
    count = CFBasicHashGetCountOfKey( table, obj );
    return count;
case OPERATION_release:
    count = CFBasicHashRemoveValue( table, obj );
    return 0 == count;

dealloc => 刪除引用計數(shù)表中的對應(yīng)記錄 => free內(nèi)存塊

=> autorelease實現(xiàn)

autorelease方法的IMP Caching


autorelease的實現(xiàn)

注意:無論調(diào)用哪一個對象的autorelease實例方法廊酣,實際上調(diào)用的都是NSObject類的autorelease實例方法。(NSAutoreleasePool類的autorelease實例方法被重載了赏枚,運行時會報錯M龀邸!6龇)

ARC - 只是自動地幫助我們處理“引用計數(shù)”的相關(guān)部分凡辱。

文件的編譯屬性設(shè)置:-fobjc-arc-fno-objc-arc

所有權(quán)修飾符

@autoreleasepool{}塊替代了NSAutoreleasePool類對象的生成持有和廢棄

__autoreleasing修飾符替代了autorelease方法的調(diào)用


autoreleasepool自動注冊
不以alloc/new/copy/mutableCopy開頭的方法(init系列方法除外)返回的對象將自動注冊
id的指針或?qū)ο蟮闹羔樤跊]有顯示指定時會被附加上__autoreleasing修飾符。
對象指針型賦值時诫睬,所有權(quán)修飾符必須一致煞茫。

id __autoreleasing *obj;
NSObject * __autoreleasing *obj;

拓展:附有__strong/__weak修飾符的變量類似于C++中的智能指針std::shared_ptr和std::weak_ptr。

ARC規(guī)則
  • ……
  • 須遵守內(nèi)存管理方法的命名規(guī)則
  • 以alloc/new/copy/mutableCopy名稱開頭的方法必須返回給調(diào)用方所應(yīng)當持有的對象
  • 以init開始的方法必須是返回類型為id/class/superclass/subclass的實例方法
  • ……
  • 顯示轉(zhuǎn)換id和void *
  • CF對象與OC對象的轉(zhuǎn)換不需要使用額外的CPU資源摄凡,所以被稱為Toll-Free Bridge
// ARC: void *p = (__bridge_retained void *)obj;
CFTypeRef CFBridgeRetain(id X) {
       return (__bridge_retained CFTypeRef)X;
}
// MRC
id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];
 
// ARC: id obj = (__bridge_transfer id)p;
id CFBridgeRelease(CFTypeRef X) {
       return (__bridge_transfer id)X;
}
// MRC
id obj = (id)p;
[obj retain];
[(id)p release];
  • CF還有以下方法:CFRetain续徽、CFReleaseCFGetRetainCountCFShow
屬性

屬性的特性修飾符必須和對應(yīng)成員變量的所有權(quán)修飾符一致G自琛G张ぁ!

// weak和默認的__strong沖突了4残鳌?颓椤!
@property (nonatomic, weak) id obj;
數(shù)組

癞己?膀斋??必須將nil賦值給所有數(shù)組元素痹雅,使得元素所賦值對象的強引用失效仰担,從而釋放那些對象;然后再使用free函數(shù)廢棄內(nèi)存塊绩社,否則會有內(nèi)存泄漏Kだ丁B该纭!

ARC實現(xiàn)

__strong

賦值分為兩種情況:alloc/new/copy/mutableCopy系列和其他
涉及的函數(shù)有:objc_msgSend贮尉、objc_releaseobjc_retain拌滋、objc_autorelease

在其他情況時,編譯器會進行優(yōu)化

__weak

修飾符功能:

  • 若附有__weak修飾符的變量所引用的對象被廢棄猜谚,則將nil賦值給該變量败砂;
  • 對象廢棄時最后調(diào)用的objc_clear_deallocating函數(shù)的動作如下
1)從weak表中獲取廢棄對象的地址作為鍵值得記錄;
2)將包含在記錄中的所有附有__weak修飾符變量的地址魏铅,賦值為nil吠卷;
3)從weak表刪除該記錄;
4)從引用計數(shù)表中刪除廢棄對象的地址為鍵值的記錄沦零。
  • 使用附有__weak修飾符的變量,即是使用注冊到autoreleasepool中的對象货岭;
  • 源代碼解讀
1) objc_loadWeakRetained函數(shù)取出附有__weak修飾符的變量所引用的對象并retain路操;
2) objc_autorelease函數(shù)將對象注冊到autoreleasepool中。
  • 最佳實踐:使用附有__weak修飾符的變量時千贯,最好先暫時賦值給附有__strong修飾符的變量后再使用屯仗;從而避免對象多次注冊到autoreleasepool中。

不能使用__weak修飾符的情況

  • iOS4及以下
  • 通過 NS_AUTOMATED_REFCOUNT_WEAK_UNAVAILABLE 聲明了不支持的類搔谴,比如 NSMachPort
  • 以下方法返回NO的時候:
 - (BOOL)allowsWeakReference;
 - (BOOL)retainWeakReference;
__autoreleasing修飾符

等同于ARC無效時調(diào)用對象的autorelease方法魁袜,即 objc_autorelease 方法的調(diào)用。


2. Blocks

  • 語法:完整形式(^T (…) { … })=> 基于推斷省略返回類型(^ (…) { … })=> 省略參數(shù)(^ { … }
    很像函數(shù)指針敦第,
  • 變量使用:使用typedef提高可讀性
  • 截獲自動變量
  • 賦值導(dǎo)致編譯錯誤(Mutable類的add方法不會峰弹!) => 解決方案:使用 __block 說明符
  • 不能截獲C語言數(shù)組 => 解決方案:使用指針

實現(xiàn)

  • 本質(zhì) OC對象(結(jié)構(gòu)體)
    • isa 類結(jié)構(gòu)指針 和 三大類型 _NSConcrete[ Stack | Malloc | Global ]Block
    • FuncPtr 函數(shù)指針
    • DescFlags 和其他
  • 截獲自動變量 - 只針對Block中使用的自動變量
    • __cself 和 OC中的 self芜果、C++中的 this
    • 自動變量的值以成員變量的形式被保存到Block的結(jié)構(gòu)體實例(或者說被其持有)鞠呈,通過__cself被使用;如果是__block變量右钾,則轉(zhuǎn)化成結(jié)構(gòu)體,其指針作為成員變量保存到Block結(jié)構(gòu)體中
    • 在Block中修改自動變量的兩種方法:
      1. 靜態(tài)變量舀射、靜態(tài)全局變量或全局變量
      2. __block存儲域類說明符 - 類似于static、auto和register說明符山林,指定將變量值設(shè)置到哪個存儲域中。

三種類型

  • _NSConcreteGlobalBlock - 存儲域:程序的數(shù)據(jù)區(qū)域
    通過以下情況得到實例
    1.記述全局變量的地方有Block語法時
    2.Block語法的表達式中不使用截獲的自動變量時
  • _NSConcreteStackBlock - 存儲域:棧捌朴;復(fù)制效果:到堆
    除Global之外的Block語法生成的都是棧Block
  • _NSConcreteMallocBlock - 存儲域:堆;復(fù)制效果:引用計數(shù)增加

實際上當ARC有效時砂蔽,多數(shù)情況編譯器會恰當?shù)嘏袛啵詣由蓪lock從棧上復(fù)制到堆上的代碼镣隶!
什么時候棧上的Block會被復(fù)制到堆上呢诡右?
 1. 調(diào)用Block的copy實例方法時
 2. Block作為函數(shù)返回值返回時
 3. 將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時
 4. 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時
需要手動復(fù)制的情形:NSArray的initWithObjects:(除4外作為方法參數(shù)時帆吻;注釋:現(xiàn)在應(yīng)該連這個也OK了,請測試2轮蟆!J缥怠)愕撰;也即是ARC萬能。


Block的廢棄和__block變量的釋放

實質(zhì)/本質(zhì)

  • Block - 棧/堆/數(shù)據(jù)區(qū)上的 Block 的結(jié)構(gòu)體實例带迟,isa指針
  • __block 變量 - 棧上 __block 變量的結(jié)構(gòu)體實例

Block超出變量作用域可存在的理由 => 將Block和__block變量從棧上復(fù)制到堆上解決
__block變量的結(jié)構(gòu)體成員變量__fowarding存在的理由 => 實現(xiàn)無論__block變量配置在棧上還是堆上都能正確地進行訪問


Block循環(huán)引用
原因:Block中附有__strong修飾符的對象類型自動變量在從棧復(fù)制到堆上時柿究,該對象會被Block所持有。
解決方案:

  1. ARC:通過 __weak__unsafe_unretained 修飾符(iOS4)來替代 __strong 類型的被截獲的自動變量
    通過 __block 說明符和設(shè)置nil來打破循環(huán)
  2. MRC:通過 __block 說明符指定變量不被Block所retain蝇摸;ARC下__block說明符的作用僅限于使其能在Block中被賦值。
"原理"
如果對block做一次copy操作, block的內(nèi)存就會在堆中
* 它會對所引用的對象做一次retain操作
* 非ARC : 如果所引用的對象用了__block修飾, 就不會做retain操作
* ARC : 如果所引用的對象用了__unsafe_unretained\__weak修飾, 就不會做retain操作

3. Grand Central Dispatch(GCD)

兩種Queue

GCD API

獲取系統(tǒng)提供的隊列:Main/Global Dispatch Queue律歼;無需內(nèi)存管理

隊列類型和轉(zhuǎn)發(fā)

使用 Concurrent Dispatch Queue 和 dispatch_barrier_async 函數(shù)可實現(xiàn)高效率的數(shù)據(jù)庫訪問和文件訪問险毁。


GCD實現(xiàn)

GCD分為Dispatch Queue和Dispatch Source兩個部分,各自的實現(xiàn)如下:

Dispatch Queue

GCD是XNU內(nèi)核級所實現(xiàn)的多線程管理API鲸鹦,根據(jù)CPU核等系統(tǒng)軟硬件情況進行了優(yōu)化的線程池跷跪,提供高性能的簡單編程接口。
(注釋:Darwin - NeXT電腦公司開發(fā)的用于NEXTSTEP的XNU內(nèi)核是兼有Mach3微內(nèi)核和大量來自BSD宏內(nèi)核的元素(進程吵瞻、網(wǎng)絡(luò)、虛擬文件系統(tǒng))以及I/O Kit的混合內(nèi)核)

Dispatch Queue實現(xiàn)
Dispatch Source

實現(xiàn):BSD系內(nèi)核慣有功能kqueue的包裝(XNU內(nèi)核事件發(fā)生時胖烛,能在應(yīng)用程序編程方執(zhí)行處理)浓恶。

"使用慣例"   
1. create or get - 獲取隊列
2. dispatch_source_create - 基于“監(jiān)聽”的內(nèi)核事件在隊列上構(gòu)建Dispatch Source
3. dispatch_source_set_( timer|event_handler|cancel_handler ) - 配置Dispatch Source
  一些列的處理方法,比如:dispatch_source_get_data九府、dispatch_source_cancel覆致、dispatch_source_release
4. dispatch_resume(source) - 啟動事件源監(jiān)聽
GCD能夠調(diào)度的事件源分類
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末煌妈,一起剝皮案震驚了整個濱河市璧诵,隨后出現(xiàn)的幾起案子仇冯,更是在濱河造成了極大的恐慌,老刑警劉巖比被,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件泼舱,死亡現(xiàn)場離奇詭異,居然都是意外死亡娇昙,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門蹲盘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來膳音,“玉大人,你說我怎么就攤上這事薄嫡『辽睿” “怎么了毒姨?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長闸迷。 經(jīng)常有香客問我俘枫,道長,這世上最難降的妖魔是什么今阳? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任盾舌,我火速辦了婚禮蘸鲸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘膝舅。我一直安慰自己窑多,他們只是感情好,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布琳轿。 她就那樣靜靜地躺著,像睡著了一般挪哄。 火紅的嫁衣襯著肌膚如雪琉闪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天斯入,我揣著相機與錄音蛀蜜,去河邊找鬼。 笑死磅摹,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的户誓。 我是一名探鬼主播幕侠,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼晤硕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起漆枚,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎软族,沒想到半個月后残制,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡颗祝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年螺戳,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盖腿。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡翩腐,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出茂卦,到底是詐尸還是另有隱情疙筹,我是刑警寧澤禁炒,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站暴备,受9級特大地震影響们豌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜障癌,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一涛浙、第九天 我趴在偏房一處隱蔽的房頂上張望摄欲。 院中可真熱鬧,春花似錦胸墙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗽冒。三九已至,卻和暖如春剿另,著一層夾襖步出監(jiān)牢的瞬間贬蛙,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工氛堕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讼稚。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓锐想,卻偏偏與公主長得像,于是被迫代替她去往敵國和親赠摇。 傳聞我的和親對象是個殘疾皇子浅蚪,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容