標籤:blog http io ar os 使用 sp strong 檔案
【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當作一個範例
看看下面的例子,有發現任何問題嗎?
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:…]這裡
很明顯的這邊的用途是要做一個event callback的用途
也就是說我給你一個block,當動作完成的時候callback我。
這是一個典型的非同步的作法。
但由於如果你要把block拿來之後使用,
你一定要呼叫[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了。
解決方法很簡單,看看ASIHttpRequest官網的檔案
也就是用__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就不會retain request
當然也就不會有retain cycle的問題。
Anti Pattern 2
Anti Pattern 1是在使用別人的library的時候容易出現的
Anti Pattern 2是在實作自己的class的時候容易出現
請看下面這段code
//MyClass.h@property <nonatomic, copy=""> MyBlock 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
而是retain self。
所以造成的就是
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以後的版本
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
block是可以捕捉內容相關的特殊代碼塊。block可以訪問定義在block外的變數,當在block中使用時,它就會為其在範圍內的每個標量變數建立一個副本。如果通過self擁有一個block,然後又在block中改變了執行個體變數,就會出錯。例如:
1 self.block = ^(NSString *aString)2 {3 self.aLabel.text = aString;4 });
這段代碼中,self保留了block,同時block又保留了self,會引發迴圈保留。很危險。如果未使用ARC,可以使用__block和__unsafe_unretained來複製一個未保留的引用副本。
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 }); 在arc出現之前,我們可以自由的把CF*對象轉成NS*對象,這稱為自己橋接。用了arc之後,我們需要指定一個所有權轉移修飾符。目前arc中提供的修飾符有:1.__bridge2.__bridge_retained3.__bridge_transfer 第一個修飾符__bridge是一個普通的轉換,表示不需要增加引用計數,不更改所有權。第二個是在轉換C指標類型時,增加引用計數的值。第三個是把Core Foundation 指標類型轉換成obj-c指標,變把引用計數值+1。如用Core Foundation 方法建立一個對象,並且要用arc來管理對象的記憶體,就可以用這個。 arc移植的常見錯誤1.強制轉換obj-c指標位C指標(或者反過來轉換)2.在arc中把void*指標強制轉成id類型(或者反過來轉),如果要轉,就必須是用修飾符例如: id selfPointer = (__bridge void *)self;3.在結構體或者(union)集合體中是用obj-c對象4.使用NSAutoreleasePool 轉自:http://blog.csdn.net/a330416020/article/details/19119491
[轉]iOS中ARC下Block的循環參考