Objective C ARC 使用及原理
手把手教你ARC?,里面介紹了ARC的一些特性, 還有將非ARC工程轉(zhuǎn)換成ARC工程的方法
ARC 蘋果官方文檔
下面用我自己的話介紹一下ARC,并將看文檔過程中的疑問和答案寫下來。下面有些是翻譯,但不是全部,請一定要看一遍官方文檔
- 不考慮 iOS4 的 ARC 規(guī)則
簡單地說,ARC在編譯時刻為代碼在合適的位置加上retain 和 release. 復(fù)雜點,它還提供其它一些功能,還為解決一些問題,添加了一些關(guān)鍵字和功能,后面會說。
ARC強制要求的新規(guī)則
- 不可以調(diào)用dealloc, 不可以實現(xiàn)或者調(diào)用retain, release, retainCount, autorelease.
- 不可以使用NSAllocateObject, NSDeallocateObject
-
在C 結(jié)構(gòu)體里不可以使用 Objective C 對象,比如下面是不允許的:
struct A {NSString *string;}; -
id 和 void * 之間沒有自然的轉(zhuǎn)換
id 指的是 Objective C 對象
void * 指的是 C指針, CGColorRef這種東西
id 和 void *之間賦值要添加 __bridge 系列關(guān)鍵字(后面說) -
不可以使用 NSAutoreleasePool 用 @autoreleasepool 替代
- 不可以使用 memory zones (NSZone). (表示沒認(rèn)真用過)
-
不可以給屬性取new開頭的名字, 除非給它起個不是new開頭的getter. (原因不明)
// Won't work@property NSString *newTitle;// words:@property (getter=theNewTitle) NSString *newTitle;
屬性聲明
引入了weak, strong, unsafe_unretained, 去掉了retain, 保留了assign 其余不變
- strong 相當(dāng)于 MRC(Manual Reference Counting) 的 retain
- weak 相當(dāng)于 MRC 的 assign 但是 在指向的對象被銷毀的時候,指針會被設(shè)置成0
-
assign 屬性 如果是Objective C 對象,在沒有特殊處理的時候,相當(dāng)于strong
下面代碼在 MRC 情況下是弱引用的
@interface MyClass : Superclass {id thing; // Weak reference.}// ...@end@implementation MyClass- (id)thing {return thing;} - (void)setThing:(id)newThing {thing = newThing;}// ...@end但是在 ARC 情況下則不是 id thing; 被轉(zhuǎn)換成了 id __strong thing;
所以要把上面代碼的 id thing; 改為 id __weak thing; 才接近原來assign的意思. -
unsafe_unretained 和原來的 assign行為最像.
- 對于手動寫 setter getter 又設(shè)置了修飾符的情況,我沒有研究
在這里我想到好多問題
聲明了屬性@property (weak) NSString *member 對應(yīng)的成員變量 NSString *_member (成員變量里沒有寫__weak)會怎樣
答案是編譯出錯屬性@property (weak) NSString *member 和 成員變量 NSString *_member123 (成員變量里沒有寫__weak) @synthesize 在一起會怎樣
答案是寫著的時候xcode就顯示出了錯誤其余問題可以從上面兩個問題和答案推導(dǎo).
變量修飾符
下面的變量表示Objective C對象變量
-
__strong
默認(rèn),變量在,對象在
-
__weak
對象在,變量可以安全使用對象, 對象銷毀,變量被設(shè)置為nil
-
__unsafe_unretained
對象在不在和變量無關(guān), 變量在不在和對象無關(guān),如果對象被銷毀了,還通過變量想使用對象,會崩潰,是不安全的。(意會)
-
__autorelease
表示指向的對象是autorelease的, 例子如下
In non-ARC Programming, the save function looks like this:- (BOOL)save:(NSError * __autoreleasing *)myError {*myError = [[[NSError alloc] initWith…] autorelease]}In ARC Programming, the save function looks like this:- (BOOL)save:(NSError * __autoreleasing *)myError {*myError = [[NSError alloc] initWith…];}ARC代碼
NSError *error;BOOL OK = [myObject performOperationWithError:&error];被轉(zhuǎn)換成
NSError * __strong error;NSError * __autoreleasing tmp = error;BOOL OK = [myObject performOperationWithError:&tmp];error = tmp;其實可以直接使用 NSError * __autoreleasing error; 來增加效率。
ARC根據(jù)上面那些修飾符自動生成額外的代碼。
對于 __strong
NSNumber * __strong number = [NSNumber numberWithInt:13];將會被編譯成
NSNumber * number = [[NSNumber numberWithInt:13] retain]; …… // 在 number 所在的定義域外, 或者 number = nil 的時候。 [number release];__weak 后面再說
__unsafe_unretained 應(yīng)該是什么都不干 (猜測)
對于 __autorelease
NSNumber * __autorelease number = [[NSNumber alloc] initWithInt:13];將會被編譯成
NSNumber * number = [[[NSNumber alloc] initWithInt:13] autorelease];修飾符還可以告訴編譯器代碼的行為
例如
NSError * __autoreleasing tmp = error; BOOL OK = [myObject performOperationWithError:&tmp];myObject 的 performOperationWithError 使用的可能是 MRC 的代碼, 也可能是 ARC的代碼, 但它參數(shù)返回的肯定是一個autorelease的對象。 有了 __autoreleasing 修飾, 編譯器可以知道從 performOperationWithError 方法獲得 tmp 后不需要處理它的內(nèi)存問題。
__weak 的實現(xiàn)
__weak 指針在對象被銷毀的時候會被設(shè)置成 nil, 這個功能很好很強大, 避免了很多問題, 但看上去不是在某處插入個 [obj release]; obj = nil; 就可以實現(xiàn)了的。
具體實現(xiàn)在?How does the ARC's zeroing weak pointer behavior implemented?有說。
這里簡單介紹一下:
ARC 的 NSObject 里大概添加了一個 weak 指針的數(shù)組,當(dāng)對象銷毀的時候,把數(shù)組里的變量都設(shè)置為 nil (表述不嚴(yán)謹(jǐn),大概就是這個意思)防止循環(huán)引用和長時間過程中被銷毀(下面可能存在誤導(dǎo),要批判地看!)
在 MRC 中 __block id x = y; block 將不會 [x retain]; 在block執(zhí)行完之后也不會 [x release];
在 ARC 中 __block id x = y; 應(yīng)該等于 __strong __block id x = y; 這樣會有一個retain的過程,在block被銷毀的時候 [x release]。
從上面可以知道使用 __block id x = y; 而x如果擁有block的copy, 不進行處理會造成循環(huán)引用。
于是 apple 告訴我們可以這樣寫:
MyViewController * __block myController = [[MyViewController alloc] init…]; // ... myController.completionHandler = ^(NSInteger result) {[myController dismissViewControllerAnimated:YES completion:nil];myController = nil; };但對于多次調(diào)用的情況,上面無法達(dá)到目的。可以用?weak 關(guān)鍵字來替代?block (注意是替代,不是合在一起用,我對合在一起用沒研究)
MyViewController *myController = [[MyViewController alloc] init…]; // ... MyViewController * __weak weakMyViewController = myController;myController.completionHandler = ^(NSInteger result) {[weakMyViewController dismissViewControllerAnimated:YES completion:nil]; };上面還是會造成問題, 如果block的代碼會執(zhí)行很長時間, 在那段時間 weakMyViewController 被銷毀了, 那么它就變成了 nil. 程序不是崩潰就是得到錯誤的結(jié)果, 這樣是不行的,apple 也提供了解決方法
MyViewController *myController = [[MyViewController alloc] init…]; // ... MyViewController * __weak weakMyController = myController; myController.completionHandler = ^(NSInteger result) {MyViewController *strongMyController = weakMyController;if (strongMyController) {// ...[strongMyController dismissViewControllerAnimated:YES completion:nil];// ...}else {// Probably nothing...} };到此表面問題基本解決。
一個古怪的行為
有代碼如下:
@interface AA : NSObject @property (strong) NSString *string; @end@implementation AA - (void)dealloc {NSLog(@"%@ dealloc", self.string); } @end- (void)testArcSimple {AA * __strong aa_weak_holder = [[AA alloc] init];AA * __weak aa_weak = aa_weak_holder;aa_weak_holder.string = @"aa_weak";void (^aBlock)(void) = ^(){NSLog(@"block : %@", aa_weak.string);};aa_weak_holder = nil;aBlock(); }上面的輸出是
block : aa_weak aa_weak dealloc // 這個log出現(xiàn)在一個runloop的最后而下面這段代碼:
AA * __strong aa_weak_holder = [[AA alloc] init]; aa_weak_holder.string = @"weak"; AA * __weak aa_weak = aa_weak_holder; aa_weak_holder = nil; NSLog(@"aa : %@", aa_weak);輸出為
weak dealloc aa : (null)猜測是 aa_weak 在
void (^aBlock)(void) = ^(){NSLog(@"block : %@", aa_weak.string);};的位置 [[aa_weak retain] autorelease] 了一遍。表示不懂。
Toll-Free Bridging
- __bridge : Objective-C 和 Core Foundation 之間的轉(zhuǎn)換, 擁有權(quán)不變。
- __bridge_retained : 從 Objective-C 到 Core Foundation 的轉(zhuǎn)換,由程序員負(fù)責(zé)把得到的 CFxxxRef 銷毀
- __bridge_transfer : 從 Core Foundation 到 Objective-C 的轉(zhuǎn)換,由ARC負(fù)責(zé)把得到的 id 銷毀
__bridge_retained 的作用等于 CFBridgingRetain
__bridge_transfer 的作用等于 CFBridgingRelease
Cocoa 方法返回的 CF 對象
比如 [[UIColor greenColor] CGColor]; 編譯器知道返回的 CFxxxRef 是不是需要 release 的, 當(dāng)需要把它在此轉(zhuǎn)換成 Cocoa 對象的時候, 不必用 __bridge __bridge_transfer 這樣的修飾符, 但需要顯式寫出要轉(zhuǎn)換成的類型, 比如:
UIColor *color = (id)[UIColor greenColor].CGColor; // 雖然這樣比較無聊。?
?
****************
來自楊先生的分享...
****************
posted on 2015-08-11 21:12 yanshanLove 閱讀(...) 評論(...) 編輯 收藏轉(zhuǎn)載于:https://www.cnblogs.com/Lxiaolong/p/4722351.html
超強干貨來襲 云風(fēng)專訪:近40年碼齡,通宵達(dá)旦的技術(shù)人生總結(jié)
以上是生活随笔為你收集整理的Objective C ARC 使用及原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 6最好的之一 HTML5/CSS3 演示
- 下一篇: HDU 4588 Count The C