【ARC的特性】
ARC下,所有NSObject类型指针,
1. 默认为__strong类型
2. 可以显示的指定为__weak类型,__weak类型指针在所指向对象销毁后会自动置为nil
3. __autorelesing类型用于inout参数类型
ARC下,当一个函数返回一个NSObject指针时,编译器会帮我们实现autorelease调用。例如:
return pObject;
编译器会帮我们扩展为 return [pObject autorelease];
ARC下,不能显式release,可以使用将值赋为nil来让编译器为我们release。
【ARC与Block】
Block的生命周期管理非常的微妙,与ARC混在一起后,更加复杂。
当Block延stack向上(up)传递的时候,直接返回,编译器会添加[[ copy] autorelease]代码。
当Block延stack向下传递给需要retain的容器的时候,需要显式的调用[^{} copy]方法。
在ARC下,__block修改的NSObject指针依然会被retain。
在ARC下,一个block内引用一个对象的实例变量后,self会被retain,所以极易造成strong reference cycle,可以通过__weak指针来避免这种情形,因为ARC不会为__weak指针retain。
在iOS4.0推出了Blocks這個語言特性後
到現在iOS都已經出到5.0了所以我想Blocks應該可以被廣泛應用了但現在iOS環境是從MRC(Manual Reference Counting) 走到ARC (Automatic Reference Counting)在Reference Counting的環境中Runtime是無法自動解除Retain cycle的而Blocks有很多隱性的retain的動作很容易不小心的造成retain cycle。而本篇的重點是點出三種會造成Retain Cycle的Anti-patterns再來講一下怎麼解決。在討論之前還是先大概重述一些概念block當中是允許去使用外部的variable但是local variable是會自動做retain的動作例如MyClass* foo = ….;self.someBlock = ^{ [foo bar];};
上面的foo在此block被copy到heap的時候
也會一起被自動retain而這就是我說的很容易造成retain cycle的主因。Anti Pattern 1第一個例子我們先用大家很常用的Opne source library 當作一個範例看看下面的例子,有發現任何問題嗎?ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];[request setCompletionBlock:^{ // Use when fetching text data NSString *responseString = [request responseString]; // Use when fetching binary data NSData *responseData = [request responseData];}];
這邊我們要首先要注意的點就是[request setCompletionBlock:…]
這裡
[aBlock copy]
的動作,此動作會把block從stack丟進heap。因為在iOS的環境block也是一個object,此時這個block的retain count就會增加1這時候根據定義,這個block中參照的request
這個local變數就也會被retain起來。所以request
的retain count也會增加1。但問題來了,一旦request
完成任務,應當要被release的時候卻會發現retain count始終無法歸零。理由是request <-> block 這兩個互相retain而無法正常釋放,這就是所謂的retain cycle了。 解決方法很簡單,看看
也就是用__block
來描述request
__block ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];[request setCompletionBlock:^{ // Use when fetching text data NSString *responseString = [request responseString]; // Use when fetching binary data NSData *responseData = [request responseData];}];
通過block variable不會retain的特性,
有點類似weak reference的作用此時block就不會retainrequest
當然也就不會有retain cycle的問題。
//MyClass.h@propertyMyBlock onCompleteBlock;//MyClass.cself.onCompleteBlock = ^{ [self doSomething];}
我相信這邊大家已經馬上看出問題在哪裡了
其實Anti Pattern2算是Anti Pattern 1的特例只是這邊使用的是特殊變數self但有些時候我們更容易忽略的是在block中始用自己的member variable例如//MyClass.h@interface MyClass : NSObject{ NSDate* lastModifed;}//MyClass.cself.onCompleteBlock = ^{ lastModifed = [[NSDate date] retain];}
這時候就沒有那麼容易察覺了。
根據定義,在使用block的時候,如果我們使用到member variable,此時retain的不是lastModified指到的object而是retainself
。所以造成的就是self <-> block 互相retain跟anti pattern 1一樣的結果就是無法最終釋放記憶體。這時候的解決方法也是一樣是拿出__block
來用 //MyClass.c__block MyClass* tempSelf = self;self.onCompleteBlock = ^{ tempSelf.lastModifed = [NSDate date];}Anti Pattern 3繼續看下面的code
SettingsViewController* settingsViewController = [[[SettingsViewController alloc] init] autorelease];settingsViewController.onUpdate = ^{ [self doUpdate];}self.settingsViewController = settingsViewController;
雖然這個Block中沒有直接使用到settingsViewController,感覺應該不會有retain cycle
但是因為self -> settingsViewController而setttingsViewController -> block再來block -> self這就剛好繞了一圈,同樣會有retain cycle。所以呢,還是要想一樣用anti pattern 2的解法去解決
//RootViewController.mSettingsViewController* settingsViewController = [[[SettingsViewController alloc] init] autorelease];__block RootViewController* tempSelf = self;settingsViewController.onUpdate = ^{ [tempSelf doUpdate];}self.settingsViewController = settingsViewController;
在reference counting的環境裡,
我建議要解決retain cycle的最好思維就是想清楚從屬關係例如最後一個anti pattern他們的從屬關係應該就是RootViewController -> SettingsViewController -> block如果block要用到SettingsViewController或是RootViewController,則就要使用weak reference (也就是__block
)在這樣的原則之下,就可以知道哪些要給他retain哪些不要了。最後要補充一點就是上面的例子都是在MRC環境下當做範例在MRC中__block
variable在block中使用是不會retain的但是ARC中__block
則是會Retain的。取而代之的是用__weak
或是__unsafe_unretained
來更精確的描述weak reference的目的其中前者只能在iOS5之後可以使用,但是比較好 (該物件release之後,此pointer會自動設成nil)而後者是ARC的環境下為了相容4.x的解決方案。所以上面的範例中 __block MyClass* temp = …; // MRC環境下使用__weak MyClass* temp = …; // ARC但只支援iOS5.0以上的版本__unsafe_retained MyClass* temp = …; //ARC且可以相容4.x以後的版本
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 self.block = ^(NSString *aString)2 {3 self.aLabel.text = aString;4 });
1 //例如:(无ARC) 2 __block id safeSelf = self; 3 self.block = ^(NSString *aString) 4 { 5 safeSelf.aLabel.text = aString; 6 }); 7 8 //(有ARC) 9 __weak id safeSelf = self; //ios 510 // __unsafe_unretained id safeSelf = self; //ios 411 self.block = ^(NSString *aString)12 {13 safeSelf.aLabel.text = aString;14 });