《Objective-C高級(jí)編程》Blocks 閱讀筆記 item7(截獲對(duì)象)

《Objective-C高級(jí)編程》Blocks 閱讀筆記系列

《Objective-C高級(jí)編程》Blocks 閱讀筆記 item1(Blocks概要和模式)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item2(Block的實(shí)質(zhì))
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item3(截獲自動(dòng)變量值)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item4(__block說(shuō)明符)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item5(Block存儲(chǔ)域)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item6(__block變量存儲(chǔ)域)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item7(截獲對(duì)象)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item8(__block變量和對(duì)象)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item9(Block循環(huán)引用)
《Objective-C高級(jí)編程》Blocks 閱讀筆記 item10(copy/release實(shí)例方法)

2.3 Blocks的實(shí)現(xiàn)

2.3.6 截獲對(duì)象

{
  id array = [[NSMutableArray alloc] init];
}

該源代碼生成并持有NSMutableArray類的對(duì)象,但是附有__strong修飾符的賦值目標(biāo)(變量array)變量作用域立即就會(huì)結(jié)束,因此對(duì)象被立即釋放并廢棄。

blk_t blk;

{
  id array = [[NSMutableArray alloc] init];
  blk = [^(id obj){
  
      [array addObject:obj];
      
      NSLog(@"array count = %ld", [array count]);
  } copy]; // 調(diào)用copy方法(Block從棧復(fù)制到堆)
}

blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);
blk([[NSObject alloc] init]);

變量作用域結(jié)束的同時(shí),變量array被廢棄,其對(duì)NSMutableArray類的對(duì)象的強(qiáng)引用失效,因此NSMutableArray類的對(duì)象被釋放并廢棄(此處我不確定是否會(huì)被廢棄)。但是,該源代碼運(yùn)行正常,執(zhí)行結(jié)果如下:

array count = 1
array count = 2
array count = 3

這意味著賦值給變量array的NSMutableArray類的對(duì)象在Block的執(zhí)行部分超出其變量作用域而存在。
經(jīng)clang轉(zhuǎn)換:

/* Block的結(jié)構(gòu)體 / 函數(shù)部分  */

// 結(jié)構(gòu)體 __main_block_impl_0
struct __main_block_impl_0 {
    // 成員變量
    struct __block_impl impl;
    struct __main_block_desc_0* Desc;
    id __strong array; 
    /*
    理解:
    1. 被NSMutableArray類對(duì)象并被截獲的自動(dòng)變量array,是附有__strong修飾符的成員變量。在Objective-C中,C語(yǔ)言結(jié)構(gòu)體不能含有附有__strong修飾符的變量。因?yàn)榫幾g器不知道何時(shí)進(jìn)行C語(yǔ)言結(jié)構(gòu)體的初始化和廢棄操作,不能很好地管理內(nèi)存。
    2. 但是,Objective-C的運(yùn)行時(shí)庫(kù)能準(zhǔn)確把握Block從棧復(fù)制到堆以及堆上的Block被廢棄的時(shí)機(jī),因此Block的結(jié)構(gòu)體即時(shí)含有附有__stong修飾符或__weak修飾符的變量,也可以恰當(dāng)?shù)剡M(jìn)行初始化和廢棄。
    */
    
    // 構(gòu)造函數(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ù)將“對(duì)象類型對(duì)象”賦值給Block的結(jié)構(gòu)體成員變量array中并持有該對(duì)象
    2. _Block_object_assign函數(shù)調(diào)用“相當(dāng)于ratain實(shí)例方法的函數(shù)”,將“對(duì)象”賦值在對(duì)象類型的結(jié)構(gòu)體成員變量中。
    */
}

// 靜態(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的結(jié)構(gòu)體成員變量array中的對(duì)象。
    2. _Block_object_dispose函數(shù)調(diào)用相當(dāng)于release實(shí)例方法的函數(shù),釋放賦值在對(duì)象類型的結(jié)構(gòu)體成員變量中的對(duì)象。
    */
}

// 靜態(tài)結(jié)構(gòu)體 __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結(jié)構(gòu)體成員變量copy和dispose中,但是在轉(zhuǎn)換后的源代碼中,這些函數(shù)包括使用指針全都沒(méi)有被調(diào)用。
2. 而是,在Block從棧復(fù)制到堆時(shí)以及堆上的Block被廢棄時(shí)會(huì)調(diào)用這些函數(shù)。
*/

/* Block語(yǔ)法,使用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]);

表 調(diào)用copy函數(shù)和dispose函數(shù)的時(shí)機(jī)

函數(shù) 調(diào)用時(shí)機(jī)
copy函數(shù) 棧上的Block復(fù)制到堆時(shí)
dispose函數(shù) 堆上的Block被廢棄時(shí)

*** 何時(shí)棧上的Block會(huì)復(fù)制到堆 ***

  • 調(diào)用Block的copy實(shí)例方法時(shí)
  • Block作為函數(shù)返回值返回時(shí)
  • 將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時(shí)
  • 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時(shí)

在棧上的Block被復(fù)制到堆時(shí)copy函數(shù)被調(diào)用,而在釋放復(fù)制到堆上的Block后,誰(shuí)都不持有Block而被廢棄時(shí),dispose函數(shù)被調(diào)用。正因?yàn)檫@種構(gòu)造,通過(guò)使用附有__strong修飾符的自動(dòng)變量,Block中截獲的對(duì)象才能給超出其變量作用域而存在。

*** 如何區(qū)分copy函數(shù)和dispose函數(shù)的對(duì)象類型 ***


表 截獲對(duì)象時(shí)和使用__block變量時(shí)的不同

對(duì)象 BLOCK_FIELD_IS_OBJECT
__block變量 BLOCK_FIELD_IS_BYREF

通過(guò)BLOCK_FIELD_IS_OBJECT和BLOCK_FIELD_IS_BYREF參數(shù),區(qū)分copy函數(shù)和dispose函數(shù)的對(duì)象類型是對(duì)象還是__block變量。

但是,與copy函數(shù)持有被截獲的對(duì)象,dispose函數(shù)釋放截獲的對(duì)象相同,copy函數(shù)持有所使用的__block變量,dispose函數(shù)釋放所使用的__block。

由此可知,Block中使用的賦值給附有__stong修飾符的自動(dòng)變量的對(duì)象和復(fù)制到堆上的__block變量,由于被堆上的Block所持有,因而可超出其變量作用域而存在。

*** 何種情形下,不調(diào)用Block的copy實(shí)例方法 ***
在Block使用對(duì)象類型自動(dòng)變量是,除以下情形外,推薦調(diào)用Block的copy實(shí)例方法:

  • Block作為函數(shù)返回值返回時(shí)
  • 將Block賦值給附有__strong修飾符id類型的類或Block類型成員變量時(shí)
  • 在方法名中含有usingBlock的Cocoa框架方法或GCD的API中傳遞Block時(shí)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容