工具:利用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)
=> 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
注意:無論調(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
续徽、CFRelease
、CFGetRetainCount
和CFShow
屬性
屬性的特性修飾符必須和對應(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_release
和objc_retain
拌滋、objc_autorelease
__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ù)指針
-
Desc
、Flags
和其他
-
- 截獲自動變量 - 只針對Block中使用的自動變量
-
__cself
和 OC中的self
芜果、C++中的this
- 自動變量的值以成員變量的形式被保存到Block的結(jié)構(gòu)體實例(或者說被其持有)鞠呈,通過
__cself
被使用;如果是__block變量右钾,則轉(zhuǎn)化成結(jié)構(gòu)體,其指針作為成員變量保存到Block結(jié)構(gòu)體中 - 在Block中修改自動變量的兩種方法:
- 靜態(tài)變量舀射、靜態(tài)全局變量或全局變量
-
__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萬能。
實質(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所持有。
解決方案:
- ARC:通過
__weak
或__unsafe_unretained
修飾符(iOS4)來替代__strong
類型的被截獲的自動變量
通過__block
說明符和設(shè)置nil來打破循環(huán) - 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)
GCD API
獲取系統(tǒng)提供的隊列:Main/Global Dispatch Queue律歼;無需內(nèi)存管理
使用 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 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)聽