《Objective-C高級編程》Blocks 閱讀筆記系列
《Objective-C高級編程》Blocks 閱讀筆記 item1(Blocks概要和模式)
《Objective-C高級編程》Blocks 閱讀筆記 item2(Block的實質)
《Objective-C高級編程》Blocks 閱讀筆記 item3(截獲自動變量值)
《Objective-C高級編程》Blocks 閱讀筆記 item4(__block說明符)
《Objective-C高級編程》Blocks 閱讀筆記 item5(Block存儲域)
《Objective-C高級編程》Blocks 閱讀筆記 item6(__block變量存儲域)
《Objective-C高級編程》Blocks 閱讀筆記 item7(截獲對象)
《Objective-C高級編程》Blocks 閱讀筆記 item8(__block變量和對象)
《Objective-C高級編程》Blocks 閱讀筆記 item9(Block循環(huán)引用)
《Objective-C高級編程》Blocks 閱讀筆記 item10(copy/release實例方法)
2.3 Blocks的實現(xiàn)
2.3.6 截獲對象
{
id array = [[NSMutableArray alloc] init];
}
該源代碼生成并持有NSMutableArray類的對象,但是附有__strong修飾符的賦值目標(變量array)變量作用域立即就會結束,因此對象被立即釋放并廢棄。
blk_t blk;
{
id array = [[NSMutableArray alloc] init];
blk = [^(id obj){
[array addObject:obj];
NSLog(@"array count = %ld", [array count]);
} copy]; // 調用copy方法(Block從棧復制到堆)
}
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);
變量作用域結束的同時须喂,變量array被廢棄,其對NSMutableArray類的對象的強引用失效,因此NSMutableArray類的對象被釋放并廢棄(此處我不確定是否會被廢棄)。但是伐弹,該源代碼運行正常,執(zhí)行結果如下:
array count = 1
array count = 2
array count = 3
這意味著賦值給變量array的NSMutableArray類的對象在Block的執(zhí)行部分超出其變量作用域而存在榨为。
經(jīng)clang轉換:
/* Block的結構體 / 函數(shù)部分 */
// 結構體 __main_block_impl_0
struct __main_block_impl_0 {
// 成員變量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
id __strong array;
/*
理解:
1. 被NSMutableArray類對象并被截獲的自動變量array惨好,是附有__strong修飾符的成員變量。在Objective-C中随闺,C語言結構體不能含有附有__strong修飾符的變量日川。因為編譯器不知道何時進行C語言結構體的初始化和廢棄操作,不能很好地管理內存板壮。
2. 但是逗鸣,Objective-C的運行時庫能準確把握Block從棧復制到堆以及堆上的Block被廢棄的時機合住,因此Block的結構體即時含有附有__stong修飾符或__weak修飾符的變量绰精,也可以恰當?shù)剡M行初始化和廢棄撒璧。
*/
// 構造函數(shù)
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, id __strong_array, int flags =0) : array(_array){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 靜態(tài)函數(shù) __main_block_func_0
static void __main_block_func_0(struct __main_block_impl_0 *__cself, id obj)
{
id __strong array = __cself->array;
[array addObject:obj];
NSLog(@"array count = %ld", [array count]);
}
// 靜態(tài)函數(shù) __main_block_copy_0
static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src){
_Block_object_assign(&dst->array, src->array, BLOCK_FIELD_IS_OBJECT);
/*
理解:
1. __main_block_copy_0函數(shù)使用_Block_object_assign函數(shù)將“對象類型對象”賦值給Block的結構體成員變量array中并持有該對象
2. _Block_object_assign函數(shù)調用“相當于ratain實例方法的函數(shù)”,將“對象”賦值在對象類型的結構體成員變量中笨使。
*/
}
// 靜態(tài)函數(shù) __main_block_dispose_0
static void __main_block_dispose_0(struct __main_block_impl_0 *src){
_Block_object_dispose(src->array, BLOCK_FIELD_IS_OBJECT);
/*
理解:
1. __main_block_dispose_0函數(shù)使用_Block_object_dispose函數(shù)卿樱,釋放賦值在Block的結構體成員變量array中的對象。
2. _Block_object_dispose函數(shù)調用相當于release實例方法的函數(shù)硫椰,釋放賦值在對象類型的結構體成員變量中的對象繁调。
*/
}
// 靜態(tài)結構體 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0,
__main_block_dispose_0
};
/*
理解:
1. __main_block_copy_0函數(shù)(copy函數(shù))和__main_block_dispose_0函數(shù)(dispose函數(shù))指針被賦值__main_block_desc_0結構體成員變量copy和dispose中,但是在轉換后的源代碼中靶草,這些函數(shù)包括使用指針全都沒有被調用蹄胰。
2. 而是,在Block從棧復制到堆時以及堆上的Block被廢棄時會調用這些函數(shù)奕翔。
*/
/* Block語法裕寨,使用Block部分 */
blk_t blk;
{
id __strong array = [[NSMutableArray alloc] init];
blk = &__main_block_impl_0(__main_block_func_0, &__mian_block_desc_0_DATA, array, 0x22000000);
blk = [blk copy];
}
(*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);
(*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);
(*blk->impl.FuncPtr)(blk, [[NSObject alloc] init]);
表 調用copy函數(shù)和dispose函數(shù)的時機
函數(shù) | 調用時機 |
---|---|
copy函數(shù) | 棧上的Block復制到堆時 |
dispose函數(shù) | 堆上的Block被廢棄時 |
*** 何時棧上的Block會復制到堆 ***
- 調用Block的copy實例方法時
- Block作為函數(shù)返回值返回時
- 將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時
- 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時
在棧上的Block被復制到堆時,copy函數(shù)被調用派继,而在釋放復制到堆上的Block后宾袜,誰都不持有Block而被廢棄時,dispose函數(shù)被調用驾窟。正因為這種構造庆猫,通過使用附有__strong修飾符的自動變量,Block中截獲的對象才能給超出其變量作用域而存在绅络。
*** 如何區(qū)分copy函數(shù)和dispose函數(shù)的對象類型 ***
表 截獲對象時和使用__block變量時的不同
對象 | BLOCK_FIELD_IS_OBJECT |
---|---|
__block變量 | BLOCK_FIELD_IS_BYREF |
通過BLOCK_FIELD_IS_OBJECT和BLOCK_FIELD_IS_BYREF參數(shù)月培,區(qū)分copy函數(shù)和dispose函數(shù)的對象類型是對象還是__block變量。
但是恩急,與copy函數(shù)持有被截獲的對象节视,dispose函數(shù)釋放截獲的對象相同,copy函數(shù)持有所使用的__block變量假栓,dispose函數(shù)釋放所使用的__block寻行。
由此可知,Block中使用的賦值給附有__stong修飾符的自動變量的對象和復制到堆上的__block變量匾荆,由于被堆上的Block所持有拌蜘,因而可超出其變量作用域而存在。
*** 何種情形下牙丽,不調用Block的copy實例方法 ***
在Block使用對象類型自動變量是简卧,除以下情形外,推薦調用Block的copy實例方法:
- Block作為函數(shù)返回值返回時
- 將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時
- 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時