__block變數和對象
__block id obj = []; __block id __strong obj = []; 上述兩行代碼是等同的,ARC有效時候,id類型以及物件類型變數必定會附加所有權修飾符,預設為附有__strong修飾符。 看一下clang轉換的代碼: //__block變數用結構體部分 struct __Block_byref_obj_0 { void *__isa; __Block_byref_obj_0 *__forwarding; int __flags; int __size; void (*__Block_byref_id_object_copy)(void*, void*); void (*__Block_byref_id_object_dispose)(void*); __strong id obj; };
static void __Block_byref_id_object_copy_131(void* dst, void *src) { _Block_object_assign((char*)dst + 40, *(void**)((char*)src + 40), 131); }
static void __Block_byref_id_object_dispose_131(void *src) { _Block_object_dispose(*(void**)((vhar*)src + 40), 131); }
//__block變數聲明部分 __Block_byref_obj_0 obj = { 0, &obj, 0x2000000, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, [[NSObject alloc] init] };
當Block從棧賦值到堆時,使用_Block_object_assign函數,持有Block截獲的對象。 當堆上的Block被廢棄時,使用_Block_object_dispose函數,釋放Block截獲的對象。
只有__strong怎能沒有__weak呢? blk_t blk; { id array = [[NSMutableArray alloc] init]; id _weak array2 = array; blk = [^(id obj){ [array2 addObject:obj]; NSLog(@"array2 count = %d", [array2 count]); } copy]; } blk([[NSObject alloc] init]); blk([[NSObject alloc] init]); blk([[NSObject alloc] init]);
此時執行結果都是0. 這是因為 附有__strong修飾符的變數array在該變數範圍結束的同時被釋放、廢棄,nil被賦值在附有__weak修飾符的變數array2中。 如下同時__block __weak 雙修飾符: blk_t blk; { id array = [[NSMutableArray alloc] init]; __block id _weak array2 = array; blk = [^(id obj){ [array2 addObject:obj]; NSLog(@"array2 count = %d", [array2 count]); } copy]; } blk([[NSObject alloc] init]); blk([[NSObject alloc] init]); blk([[NSObject alloc] init]);
執行結果與之前相同。 因為即使附加了__block說明符,附有__strong修飾符的變數array也會在該變數範圍結束的同時非釋放廢棄,nil被賦值給附有__weak修飾符的變數array2中。
還有就是__autorelease 與 __block一起使用會發生編譯錯誤。
Block循環參考
當我們在Block使用__strong修飾符的物件類型自動變數,那麼當Block從棧複製到堆時,該對象為Block所持有。 這樣容易引起循環參考: typedef void (^blk_t)(void); @interface MyObject:NSObject { blk_t blk_; } @end
@implementation MyObject - (id)init { self = [super init]; blk_ = ^{NSLog(@"self = %@", self);}; return self; } - (void)dealloc { NSLog(@"dealloc"); } @end int main() { id o = [[MyObject alloc] init]; NSLog(@"%@", o); return 0; } 代碼中,dealloc一定未被調用。 MyObject類對象的Block類型成員變數blk_持有賦值為Block的強引用。 即MyObject類對象持有Block。 init執行個體方法中執行的Block文法使用附有__strong修飾符的id類型變數self。 並且由於Block文法賦值在了成員變數blk_中, 因此通過Block文法產生在棧上的Block此時由棧複製到堆。並持有所使用的self。 self持有Block,Block持有self~~~
為了避免循環參考,可聲明附有__weak修飾符的變數,並將self賦值使用。 id __weak tmp = self; blk_ = ^{NSLog(@"self = %@", tmp);};
此時,由於Block存在時,持有該Block的MyObject類對象即賦值在變數tmp中的self必定存在,因此不需要判斷變數tmp的值是否為nil。
下面代碼也會引起迴圈: @interface MyObject:NSObject { blk_t blk_; id obj_; } @end
@implementation MyObject - (id) init { self = [super init]; blk_ = ^{NSLog(@"obj_ = %@", obj_);}; return self; } @end Block中沒有self,也同樣截獲了self,引起迴圈。 其實在block文法中使用了obj_,其實就已經截獲了self: self->obj_。 與前面一樣,我們可以聲明__weak的臨時變數來避免循環參考。
我們還可以使用__block變數來避免循環參考: typedef void (^blk_t)(void); @interface MyObject:NSObject{ blk_t blk_; } @end @implementation MyObject - (id)init { self = [super init]; __block id tmp = self; blk = ^{ NSLog(@"self = %@", tmp); tmp = nil; }; return self; } - (void)execBlock{ blk_(); } - (void)dealloc{ NSLog(@"dealloc"); } @end
int main() { id o = [[MyObject alloc] init]; [o execBlock]; return 0; }
這裡並沒有引起循環參考,但是如果不調用execBlock執行個體方法,即不執行賦值給成員變數blk_的Block,便會循環參考並引起記憶體泄露。
MyObject類對象持有Block Block持有__block變數 __block變數持有MyObject類對象。
通過執行execBlock執行個體方法,Block被執行,nil被賦值給__block變數tmp中。 因此__block變數tmp堆MyObject類對象的強引用失效。 如何避免?:
MyObject類對象持有Block Block持有__block變數。
比較一下 使用__block變數避免循環參考的方法和使用__weak修飾符及__unsafe_unretained修飾符避免循環參考: __block優點: 1、通過__block變數可控制對象的持有時間 2、在不能使用__weak修飾符的環境中不使用__unsafe_unretained修飾符即可
使用__block變數的缺點: 為避免循環參考必須執行Block。
但是當執行了Block文法,沒有執行Block路徑的時候,不能避免循環參考。 如果由於Block印發了循環參考時,根據Block的用途選擇使用__block變數、__weak修飾符或者__unsafe_unretained修飾符來避免循環參考。
但是當ARC無效時? 此時我們需要手動將Block從棧複製到堆,並且要釋放Block。 此時要使用copy和release。 在arc無效的時候,__block說明符被用來避免Block中的循環參考。這是由於當Block從棧賦值到堆時,若Block使用的變數為附有__block說明符的id類型或物件類型的自動變數,不會被retain。 若Block使用的變數為沒有__block說明符的id類型或物件類型的自動變數,就會被retain。 下列代碼在arc有效或者無效都會導致循環參考: typedef void (^blk_t)(void); @interface MyObject:NSObject{ blk_t blk_; } @end @implementation MyObject - (id)init{ self = [super init]; blk_ = ^{NSLog(@"self = %@", self);}; return self; } - (void)dealloc { NSLog(@"dealloc"); } @end
int main() { id o = [[MyObject alloc] init]; NSLog(@"%@", o); return 0; }
此時我們使用__block變數就可以避免:
__block id tmp = self; blk_ = ^{NSLog(@"self = %@", tmp);};
————2014/3/22 Beijing