Block 深入淺出

iOS Block用法和實(shí)現(xiàn)原理

《Objective-C高級(jí)編程》是一本有趣又難懂的書(shū),全書(shū)就講了引用計(jì)數(shù)Block、GCD三個(gè)概念,有趣是因?yàn)橹v原理、實(shí)現(xiàn)的部分是其它iOS專業(yè)書(shū)籍里少有的。然而每個(gè)章節(jié)不讀個(gè)三五遍還是比較難理解貫通的。本文針對(duì)其中的Block部分做些簡(jiǎn)單的筆記記錄,講述Block的用法和部分實(shí)現(xiàn)原理,詳細(xì)解說(shuō)從原書(shū)中尋。

Block概要

Block:帶有自動(dòng)變量的匿名函數(shù)。 匿名函數(shù):沒(méi)有函數(shù)名的函數(shù),一對(duì){}包裹的內(nèi)容是匿名函數(shù)的作用域。 自動(dòng)變量:棧上聲明的一個(gè)變量不是靜態(tài)變量和全局變量,是不可以在這個(gè)棧內(nèi)聲明的匿名函數(shù)中使用的,但在Block中卻可以。 雖然使用Block不用聲明類,但是Block提供了類似Objective-C的類一樣可以通過(guò)成員變量來(lái)保存作用域外變量值的方法,那些在Block的一對(duì){}里使用到但卻是在{}作用域以外聲明的變量,就是Block截獲的自動(dòng)變量。

Block常規(guī)概念

Block語(yǔ)法

Block表達(dá)式語(yǔ)法:

^ 返回值類型 (參數(shù)列表) {表達(dá)式}

例如:

   ^ int (int count) {
      return count + 1;
  };

其中,可省略部分有:

  • 返回類型,例:
^ (int count) {
      return count + 1;
  };
  • 參數(shù)列表為空,則可省略,例:
^ {
    NSLog(@"No Parameter");
};

即最簡(jiǎn)模式語(yǔ)法為:

^ {表達(dá)式}

Block類型變量

聲明Block類型變量語(yǔ)法:

返回值類型 (^變量名)(參數(shù)列表) = Block表達(dá)式

例如,如下聲明了一個(gè)變量名為blk的Block:

 int (^blk)(int) = ^(int count) {
    return count + 1;
};

當(dāng)Block類型變量作為函數(shù)的參數(shù)時(shí),寫(xiě)作:

- (void)func:(int (^)(int))blk {
  NSLog(@"Param:%@", blk);
}

借助typedef可簡(jiǎn)寫(xiě):

typedef int (^blk_k)(int);
- (void)func:(blk_k)blk {
    NSLog(@"Param:%@", blk);
}

Block類型變量作返回值時(shí),寫(xiě)作:

- (int (^)(int))funcR {
      return ^(int count) {
        return count ++;
    };
}

借助typedef簡(jiǎn)寫(xiě):

typedef int (^blk_k)(int);
- (blk_k)funcR {
    return ^(int count) {
        return count ++;
    };
}

截獲自動(dòng)變量值

Block表達(dá)式可截獲所使用的自動(dòng)變量的值。 截獲:保存自動(dòng)變量的瞬間值。 因?yàn)槭恰八查g值”,所以聲明Block之后,即便在Block外修改自動(dòng)變量的值,也不會(huì)對(duì)Block內(nèi)截獲的自動(dòng)變量值產(chǎn)生影響。 例如:

 int i = 10;
  void (^blk)(void) = ^{
    NSLog(@"In block, i = %d", i);
};
i = 20;//Block外修改變量i,也不影響B(tài)lock內(nèi)的自動(dòng)變量
blk();//i修改為20后才執(zhí)行,打印: In block, i = 10
NSLog(@"i = %d", i);//打印:i = 20

__block說(shuō)明符號(hào)

自動(dòng)變量截獲的值為Block聲明時(shí)刻的瞬間值,保存后就不能改寫(xiě)該值,如需對(duì)自動(dòng)變量進(jìn)行重新賦值,需要在變量聲明前附加__block說(shuō)明符,這時(shí)該變量稱為_(kāi)_block變量。 例如:

__block int i = 10;//i為_(kāi)_block變量,可在block中重新賦值
void (^blk)(void) = ^{
    NSLog(@"In block, i = %d", i);
};
i = 20;
blk();//打印: In block, i = 20
NSLog(@"i = %d", i);//打?。篿 = 20

自動(dòng)變量值為一個(gè)對(duì)象情況

當(dāng)自動(dòng)變量為一個(gè)類的對(duì)象,且沒(méi)有使用__block修飾時(shí),雖然不可以在Block內(nèi)對(duì)該變量進(jìn)行重新賦值,但可以修改該對(duì)象的屬性。 如果該對(duì)象是個(gè)Mutable的對(duì)象,例如NSMutableArray,則還可以在Block內(nèi)對(duì)NSMutableArray進(jìn)行元素的增刪:

NSMutableArray *array = [[NSMutableArray alloc] initWithObjects:@"1", @"2",nil ];
NSLog(@"Array Count:%ld", array.count);//打印Array Count:2
void (^blk)(void) = ^{
    [array removeObjectAtIndex:0];//Ok
    //array = [NSNSMutableArray new];//沒(méi)有__block修飾,編譯失?。?};
blk();
NSLog(@"Array Count:%ld", array.count);//打印Array Count:1

Block實(shí)現(xiàn)原理

使用Clang

Block實(shí)際上是作為極普通的C語(yǔ)言源碼來(lái)處理的:含有Block語(yǔ)法的源碼首先被轉(zhuǎn)換成C語(yǔ)言編譯器能處理的源碼,再作為普通的C源代碼進(jìn)行編譯。 使用LLVM編譯器的clang命令可將含有Block的Objective-C代碼轉(zhuǎn)換成C++的源代碼,以探查其具體實(shí)現(xiàn)方式:

clang -rewrite-objc 源碼文件名

注:如果使用該命令報(bào)錯(cuò):'UIKit/UIKit.h' file not found,可參考《Objective-C編譯成C++代碼報(bào)錯(cuò)》解決。

Block結(jié)構(gòu)

使用Block的時(shí)候,編譯器對(duì)Block語(yǔ)法進(jìn)行了怎樣的轉(zhuǎn)換?

int main() {
int count = 10;
void (^ blk)() = ^(){
    NSLog(@"In Block:%d", count);
};
blk();

}

如上所示的最簡(jiǎn)單的Block使用代碼,經(jīng)clang轉(zhuǎn)換后,可得到以下幾個(gè)部分(有代碼刪減和注釋添加):

static void __main_block_func_0(
struct __main_block_impl_0 *__cself) {
int count = __cself->count; // bound by copy

NSLog((NSString *)&__NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_d2f8d2_mi_0, 
count);

}

這是一個(gè)函數(shù)的實(shí)現(xiàn),對(duì)應(yīng)Block中{}內(nèi)的內(nèi)容,這些內(nèi)容被當(dāng)做了C語(yǔ)言函數(shù)來(lái)處理,函數(shù)參數(shù)中的__cself相當(dāng)于Objective-C中的self。

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc; //描述Block大小、版本等信息
  int count;
  //構(gòu)造函數(shù)函數(shù)
  __main_block_impl_0(void *fp,
      struct __main_block_desc_0 *desc,
      int _count,
      int flags=0) : count(_count) {
      impl.isa = &_NSConcreteStackBlock; //在函數(shù)棧上聲明,則為_(kāi)NSConcreteStackBlock
      impl.Flags = flags;
      impl.FuncPtr = fp;
      Desc = desc;
    }
};

__main_block_impl_0即為main()函數(shù)棧上的Block結(jié)構(gòu)體,其中的__block_impl結(jié)構(gòu)體聲明如下:

struct __block_impl {
  void *isa;//指明對(duì)象的Class
  int Flags;
  int Reserved;
  void *FuncPtr;
};

__block_impl結(jié)構(gòu)體,即為Block的結(jié)構(gòu)體,可理解為Block的類結(jié)構(gòu)。 再看下main()函數(shù)翻譯的內(nèi)容:

int main() {
int count = 10;
void (* blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, count));

((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

}

去除掉復(fù)雜的類型轉(zhuǎn)化,可簡(jiǎn)寫(xiě)為:

int main() {
int count = 10;
sturct __main_block_impl_0 *blk = &__main_block_impl_0(__main_block_func_0,         //函數(shù)指針
                                                       &__main_block_desc_0_DATA)); //Block大小、版本等信息

(*blk->FuncPtr)(blk);   //調(diào)用FuncPtr指向的函數(shù),并將blk自己作為參數(shù)傳入

}

由此,可以看出,Block也是Objective-C中的對(duì)象。 Block有三種類(即__block_impl的isa指針指向的值,isa說(shuō)明參考《Objective-C isa 指針 與 runtime 機(jī)制》),根據(jù)Block對(duì)象創(chuàng)建時(shí)所處數(shù)據(jù)區(qū)不同而進(jìn)行區(qū)別:

  • _NSConcreteStackBlock:在棧上創(chuàng)建的Block對(duì)象
  • _NSConcreteMallocBlock:在堆上創(chuàng)建的Block對(duì)象
  • _NSConcreteGlobalBlock:全局?jǐn)?shù)據(jù)區(qū)的Block對(duì)象

如何截獲自動(dòng)變量

上部分介紹了Block的結(jié)構(gòu),和作為匿名函數(shù)的調(diào)用機(jī)制,那自動(dòng)變量截獲是發(fā)生在什么時(shí)候呢? 觀察上節(jié)代碼中__main_block_impl_0結(jié)構(gòu)體(main棧上Block的結(jié)構(gòu)體)的構(gòu)造函數(shù)可以看到,棧上的變量count以參數(shù)的形式傳入到了這個(gè)構(gòu)造函數(shù)中,此處即為變量的自動(dòng)截獲。 因此可以這樣理解:__block_impl結(jié)構(gòu)體已經(jīng)可以代表Block類了,但在棧上又聲明了__main_block_impl_0結(jié)構(gòu)體,對(duì)__block_impl進(jìn)行封裝后才來(lái)表示棧上的Block類,就是為了獲取Block中使用到的棧上聲明的變量(棧上沒(méi)在Block中使用的變量不會(huì)被捕獲),變量被保存在Block的結(jié)構(gòu)體實(shí)例中。 所以在blk()執(zhí)行之前,棧上簡(jiǎn)單數(shù)據(jù)類型的count無(wú)論發(fā)生什么變化,都不會(huì)影響到Block以參數(shù)形式傳入而捕獲的值。但這個(gè)變量是指向?qū)ο蟮闹羔槙r(shí),是可以修改這個(gè)對(duì)象的屬性的,只是不能為變量重新賦值。

Block的存儲(chǔ)域

上文已提到,根據(jù)Block創(chuàng)建的位置不同,Block有三種類型,創(chuàng)建的Block對(duì)象分別會(huì)存儲(chǔ)到棧、堆、全局?jǐn)?shù)據(jù)區(qū)域。

void (^blk)(void) = ^{
  NSLog(@"Global Block");
};

int main() {
  blk();
  NSLog(@"%@",[blk class]);//打?。篲_NSGlobalBlock__
}

像上面代碼塊中的全局blk自然是存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū),但注意在函數(shù)棧上創(chuàng)建的blk,如果沒(méi)有截獲自動(dòng)變量,Block的結(jié)構(gòu)實(shí)例還是會(huì)被設(shè)置在程序的全局?jǐn)?shù)據(jù)區(qū),而非棧上

int main() {
    void (^blk)(void) = ^{//沒(méi)有截獲自動(dòng)變量的Block
        NSLog(@"Stack Block");
    };
    blk();
    NSLog(@"%@",[blk class]);//打印:__NSGlobalBlock__

    int i = 1;
    void (^captureBlk)(void) = ^{//截獲自動(dòng)變量i的Block
        NSLog(@"Capture:%d", i);
    };
    captureBlk();
    NSLog(@"%@",[captureBlk class]);//打?。篲_NSMallocBlock__
}

可以看到截獲了自動(dòng)變量的Block打印的類是NSGlobalBlock,表示存儲(chǔ)在全局?jǐn)?shù)據(jù)區(qū)。 但為什么捕獲自動(dòng)變量的Block打印的類卻是設(shè)置在堆上的NSMallocBlock,而非棧上的NSStackBlock?這個(gè)問(wèn)題稍后解釋。

Block復(fù)制

配置在棧上的Block,如果其所屬的棧作用域結(jié)束,該Block就會(huì)被廢棄,對(duì)于超出Block作用域仍需使用Block的情況,Block提供了將Block從棧上復(fù)制到堆上的方法來(lái)解決這種問(wèn)題,即便Block棧作用域已結(jié)束,但被拷貝到堆上的Block還可以繼續(xù)存在。 復(fù)制到堆上的Block,將_NSConcreteMallocBlock類對(duì)象寫(xiě)入Block結(jié)構(gòu)體實(shí)例的成員變量isa:

impl.isa = &_NSConcreteMallocBlock;

在ARC有效時(shí),大多數(shù)情況下編譯器會(huì)進(jìn)行判斷,自動(dòng)生成將Block從棧上復(fù)制到堆上的代碼(或者直接在堆上創(chuàng)建Block對(duì)象),以下幾種情況棧上的Block會(huì)自動(dòng)復(fù)制到堆上

  • 調(diào)用Block的copy方法
  • 將Block作為函數(shù)返回值時(shí)(MRC時(shí)此條無(wú)效,需手動(dòng)調(diào)用copy)
  • 將Block賦值給__strong修改的變量時(shí)(MRC時(shí)此條無(wú)效)
  • 向Cocoa框架含有usingBlock的方法或者GCD的API傳遞Block參數(shù)時(shí)

其它時(shí)候向方法的參數(shù)中傳遞Block時(shí),需要手動(dòng)調(diào)用copy方法復(fù)制Block。 上一節(jié)的棧上截獲了自動(dòng)變量i的Block之所以在棧上創(chuàng)建,卻是NSMallocBlock_類,就是因?yàn)檫@個(gè)Block對(duì)象賦值給了__strong修飾的變量**captureBlk(_strong是ARC下對(duì)象的默認(rèn)修飾符)。 因?yàn)樯厦嫠臈l規(guī)則,在ARC下其實(shí)很少見(jiàn)到_NSConcreteStackBlock類的Block,大多數(shù)情況編譯器都保證了Block是在堆上創(chuàng)建的,如下代碼所示,僅最后一行代碼直接使用一個(gè)不賦值給變量的Block,它的類才是NSStackBlock

 int count = 0;
blk_t blk = ^(){
    NSLog(@"In Stack:%d", count);
};

NSLog(@"blk's Class:%@", [blk class]);//打?。篵lk's Class:__NSMallocBlock__
NSLog(@"Global Block:%@", [^{NSLog(@"Global Block");} class]);//打?。篏lobal Block:__NSGlobalBlock__
NSLog(@"Copy Block:%@", [[^{NSLog(@"Copy Block:%d",count);} copy] class]);//打?。篊opy Block:__NSMallocBlock__
NSLog(@"Stack Block:%@", [^{NSLog(@"Stack Block:%d",count);} class]);//打?。篠tack Block:__NSStackBlock__

關(guān)于ARC下和MRC下Block自動(dòng)copy的區(qū)別,查看《Block 小測(cè)驗(yàn)》里幾道題目就能區(qū)分了。 另外,原書(shū)存在ARC和MRC混合講解、區(qū)分不明的情況,比如書(shū)中幾個(gè)使用到棧上對(duì)象導(dǎo)致Crash的例子是MRC條件下才會(huì)發(fā)生的,但書(shū)中沒(méi)做特殊說(shuō)明。

使用__block發(fā)生了什么

Block捕獲的自動(dòng)變量添加__block說(shuō)明符,就可在Block內(nèi)讀和寫(xiě)該變量,也可以在原來(lái)的棧上讀寫(xiě)該變量。 自動(dòng)變量的截獲保證了棧上的自動(dòng)變量被銷毀后,Block內(nèi)仍可使用該變量。 __block保證了棧上和Block內(nèi)(通常在堆上)可以訪問(wèn)和修改“同一個(gè)變量”,__block是如何實(shí)現(xiàn)這一功能的?

__block發(fā)揮作用的原理:將棧上用__block修飾的自動(dòng)變量封裝成一個(gè)結(jié)構(gòu)體,讓其在堆上創(chuàng)建,以方便從棧上或堆上訪問(wèn)和修改同一份數(shù)據(jù)。

驗(yàn)證過(guò)程: 現(xiàn)在對(duì)剛才的代碼段,加上__block說(shuō)明符,并在block內(nèi)外讀寫(xiě)變量count。

int main() {
    __block int count = 10;
    void (^ blk)() = ^(){
        count = 20;
        NSLog(@"In Block:%d", count);//打?。篒n Block:20
  };
  count ++;
  NSLog(@"Out Block:%d", count);//打?。篛ut Block:11
  blk();

}

將上面的代碼段clang,發(fā)現(xiàn)Block的結(jié)構(gòu)體__main_block_impl_0結(jié)構(gòu)如下所示:

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_count_0 *count; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_count_0 *_count, int flags=0) : count(_count->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

最大的變化就是count變量不再是int類型了,count變成了一個(gè)指向__Block_byref_count_0結(jié)構(gòu)體的指針,__Block_byref_count_0結(jié)構(gòu)如下:

struct __Block_byref_count_0 {
    void *__isa;
    __Block_byref_count_0 *__forwarding;
   int __flags;
   int __size;
   int count;
};

它保存了int count變量,還有一個(gè)指向__Block_byref_count_0實(shí)例的指針__forwarding,通過(guò)下面兩段代碼__forwarding指針的用法可以知道,該指針其實(shí)指向的是對(duì)象自身:

//Block的執(zhí)行函數(shù)
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_count_0 *count = __cself->count; // bound by ref

    (count->__forwarding->count) = 20;//對(duì)應(yīng)count = 20;
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_fafeeb_mi_0, 
    (count->__forwarding->count));
}



//main函數(shù)
int main() {
    __attribute__((__blocks__(byref))) __Block_byref_count_0 count =  {(void*)0,
        (__Block_byref_count_0 *)&count, 0, 
    sizeof(__Block_byref_count_0), 10};

    void (* blk)() = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, 
    &__main_block_desc_0_DATA, 
    (__Block_byref_count_0 *)&count, 
    570425344));

    (count.__forwarding->count) ++;//對(duì)應(yīng)count ++;

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_64_vf2p_jz52yz7x4xtcx55yv0r0000gn_T_main_fafeeb_mi_1, 
(count.__forwarding->count));

    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}

為什么要通過(guò)__forwarding指針完成對(duì)count變量的讀寫(xiě)修改? 為了保證無(wú)論是在棧上還是在堆上,都能通過(guò)都__forwarding指針找到在堆上創(chuàng)建的count這個(gè)__main_block_func_0結(jié)構(gòu)體,以完成對(duì)count->count(第一個(gè)count是__main_block_func_0對(duì)象,第二個(gè)count是int類型變量)的訪問(wèn)和修改。 示意圖如下:
image

Block的循環(huán)引用

Block的循環(huán)引用原理和解決方法大家都比較熟悉,此處將結(jié)合上文的介紹,介紹一種不常用的解決Block循環(huán)引用的方法和一種借助Block參數(shù)解決該問(wèn)題的方法。 Block循環(huán)引用原因:一個(gè)對(duì)象A有Block類型的屬性,從而持有這個(gè)Block,如果Block的代碼塊中使用到這個(gè)對(duì)象A,或者僅僅是用用到A對(duì)象的屬性,會(huì)使Block也持有A對(duì)象,導(dǎo)致兩者互相持有,不能在作用域結(jié)束后正常釋放。 解決原理:對(duì)象A照常持有Block,但Block不能強(qiáng)引用持有對(duì)象A以打破循環(huán)。 解決方法方法一: 對(duì)Block內(nèi)要使用的對(duì)象A使用__weak進(jìn)行修飾,Block對(duì)對(duì)象A弱引用打破循環(huán)。

有三種常用形式:

  • 使用__weak ClassName
__block XXViewController* weakSelf = self;
self.blk = ^{
    NSLog(@"In Block : %@",weakSelf);
};
  • 使用__weak typeof(self)
__weak typeof(self) weakSelf = self;
self.blk = ^{
    NSLog(@"In Block : %@",weakSelf);
};
  • Reactive Cocoa中的@weakify和@strongify
@weakify(self);
self.blk = ^{
    @strongify(self);
    NSLog(@"In Block : %@",self);
};

其原理參考《@weakify, @strongify》,自己簡(jiǎn)便實(shí)現(xiàn)參考《@weak - @strong 宏的實(shí)現(xiàn)》

方法二:對(duì)Block內(nèi)要使用的對(duì)象A使用__block進(jìn)行修飾,并在代碼塊內(nèi),使用完__block變量后將其設(shè)為nil,并且該block必須至少執(zhí)行一次。

 __block XXController *blkSelf = self;
self.blk = ^{
    NSLog(@"In Block : %@",blkSelf);
};

注意上述代碼仍存在內(nèi)存泄露,因?yàn)椋?/p>

  • XXController對(duì)象持有Block對(duì)象blk
  • blk對(duì)象持有__block變量blkSelf
  • __block變量blkSelf持有XXController對(duì)象
 __block XXController *blkSelf = self;
self.blk = ^{
    NSLog(@"In Block : %@",blkSelf);
    blkSelf = nil;//不能省略
};

self.blk();//該block必須執(zhí)行一次,否則還是內(nèi)存泄露

在block代碼塊內(nèi),使用完使用完__block變量后將其設(shè)為nil,并且該block必須至少執(zhí)行一次后,不存在內(nèi)存泄露,因?yàn)榇藭r(shí):

  • XXController對(duì)象持有Block對(duì)象blk
  • blk對(duì)象持有__block變量blkSelf(類型為編譯器創(chuàng)建的結(jié)構(gòu)體)
  • __block變量blkSelf在執(zhí)行blk()之后被設(shè)置為nil(__block變量結(jié)構(gòu)體的__forwarding指針指向了nil),不再持有XXController對(duì)象,打破循環(huán)

第二種使用__block打破循環(huán)的方法,優(yōu)點(diǎn)是:

  • 可通過(guò)__block變量動(dòng)態(tài)控制持有XXController對(duì)象的時(shí)間,運(yùn)行時(shí)決定是否將nil或其他變量賦值給__block變量
  • 不能使用__weak的系統(tǒng)中,使用__unsafe_unretained來(lái)替代__weak打破循環(huán)可能有野指針問(wèn)題,使用__block則可避免該問(wèn)題

缺點(diǎn)也明顯:

  • 必須手動(dòng)保證__block變量最后設(shè)置為nil
  • block必須執(zhí)行一次,否則__block不為nil循環(huán)應(yīng)用仍存在

因此,還是避免使用第二種不常用方式,直接使用__weak打破Block循環(huán)引用。 方法三:將在Block內(nèi)要使用到的對(duì)象(一般為self對(duì)象),以Block參數(shù)的形式傳入,Block就不會(huì)捕獲該對(duì)象,而將其作為參數(shù)使用,其生命周期系統(tǒng)的棧自動(dòng)管理,不造成內(nèi)存泄露。 即原來(lái)使用__weak的寫(xiě)法:

__weak typeof(self) weakSelf = self;
self.blk = ^{
    __strong typeof(self) strongSelf = weakSelf;
    NSLog(@"Use Property:%@", strongSelf.name);
    //……
};
self.blk();

改為Block傳參寫(xiě)法后:

self.blk = ^(UIViewController *vc) {
    NSLog(@"Use Property:%@", vc.name);
};
self.blk(self);

優(yōu)點(diǎn):

  • 簡(jiǎn)化了兩行代碼,更優(yōu)雅
  • 更明確的API設(shè)計(jì):告訴API使用者,該方法的Block直接使用傳進(jìn)來(lái)的參數(shù)對(duì)象,不會(huì)造成循環(huán)引用,不用調(diào)用者再使用weak避免循環(huán)

該種用法的詳細(xì)思路,和clang后的數(shù)據(jù)結(jié)構(gòu),可參考《Heap-Stack Dance》。

?著作權(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)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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