iOS-Blocks

花了一段時間對Block深入的研究了一下,以下是我邊研究邊寫的筆記記錄,其中大部分內(nèi)容都是從多線程和內(nèi)存管理那本書中而來,并加入了自己的說明與總結(jié),對Block有了比較深入的理解。如果哪里有問題,還望留言指出。

正文

什么是blocks,blocks是C語言的擴(kuò)展功能。概括為:帶有(截獲)自動變量(局部變量)的匿名函數(shù)。

擴(kuò)展
1、棧區(qū)(stack)— 由編譯器自動分配釋放 ,存放函數(shù)的參數(shù)值,局部變量的值等。其操作方式類似于數(shù)據(jù)結(jié)構(gòu)中的棧。
2、堆區(qū)(heap) — 一般由程序員分配釋放, 若程序員不釋放,程序結(jié)束時可能由os回收 。注意它與數(shù)據(jù)結(jié)構(gòu)中的堆是兩回事,分配方式倒是類似于鏈表,呵呵。
3、全局區(qū)(靜態(tài)區(qū))(static)—,全局變量和靜態(tài)變量的存儲是放在一塊的,初始化的全局變量和靜態(tài)變量在一塊區(qū)域, 未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域。 - 程序結(jié)束后有系統(tǒng)釋放。
4、文字常量區(qū) —常量字符串就是放在這里的。 程序結(jié)束后由系統(tǒng)釋放。
5、程序代碼區(qū)—存放函數(shù)體的二進(jìn)制代碼。

一、 數(shù)據(jù)結(jié)構(gòu)

為了研究編譯器是如何實現(xiàn) block 的,我們需要使用 clang。clang 提供一個命令,可以將 Objetive-C 的源碼改寫成 c 語言的,借此可以研究 block 具體的源碼實現(xiàn)方式。該命令是
clang -rewrite-objc block.c。

block的數(shù)據(jù)結(jié)構(gòu)定義如下:
第一種:

struct __block_impl{
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
};
struct __main_block_desc_0{
    unsigned long reserved;
    unsigned long Block_size;
};
struct __main_block_impl_0{
    struct __block_impl impl;
    struct __main_block_desc_0 *desc;
};

第二種:

struct __funcName_block_impl_index{
    void *isa;
    int Flags;
    int Reserved;
    void *FuncPtr;
    struct __funcName_block_desc_index *Desc;
    /* Imported variables. */
}

實際上,代碼轉(zhuǎn)化后是第一種的結(jié)構(gòu)體的形式,但是為了便于理解,我們可以直接理解為第二種形式,不過僅是結(jié)構(gòu)體的嵌套方式不一樣,而且他們在內(nèi)存上是完全一樣的。解釋:如下 2 個結(jié)構(gòu)體 SampleA 和 SampleB 在內(nèi)存上是完全一樣的,原因是結(jié)構(gòu)體本身并不帶有任何額外的附加信息。(此處解釋,來源于唐巧一篇文章中對該結(jié)構(gòu)的解釋)

struct SampleA {
    int a;
    int b;
    int c;
};
struct SampleB {
    int a;
    struct Part1 {
        int b;
    };
    struct Part2 {
        int c;
    };
};

結(jié)構(gòu)體的理解:

  1. isa 指針,所有對象都有該指針,用于實現(xiàn)對象相關(guān)的功能。指向 _NSConcreteStackBlock、_NSConcreteMallocBlock或_NSConcreteGlobalBlock類。
  2. flags,用于按 bit 位表示一些 block 的附加信息,本文后面介紹 block copy 的實現(xiàn)代碼可以看到對該變量的使用。
  3. reserved,保留變量。
  4. FuncPtr,函數(shù)指針,指向具體的 block 實現(xiàn)的函數(shù)調(diào)用地址。Block使用的匿名函數(shù)部分,實際上被轉(zhuǎn)化為簡單的c語言函數(shù)來處理。
  5. descriptor, 表示該 block 的附加描述信息,主要是 size 大小,以及 copy 和 dispose 函數(shù)的指針。
  6. variables,capture 過來的變量,block 能夠訪問它外部的局部變量,就是因為將這些變量(或變量的地址)復(fù)制到了結(jié)構(gòu)體中。

二、 實質(zhì)

一個Block實際是個結(jié)構(gòu)體實例(機(jī)構(gòu)體的結(jié)構(gòu)如上所示),block被復(fù)制給一個Block變量,該變量實際是個結(jié)構(gòu)體指針,指向該結(jié)構(gòu)體。這與oc對象的本質(zhì)就一樣了,oc對象都是個結(jié)構(gòu)體指針,所以block其實也是個oc對象。

擴(kuò)展

objc/runtime.h中objc_class和objc_object結(jié)構(gòu)體的定義如下:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                       OBJC2_UNAVAILABLE;  // 父類
    const char *name                        OBJC2_UNAVAILABLE;  // 類名
    long version                            OBJC2_UNAVAILABLE;  // 類的版本信息,默認(rèn)為0
    long info                               OBJC2_UNAVAILABLE;  // 類信息,供運行期使用的一些位標(biāo)識
    long instance_size                      OBJC2_UNAVAILABLE;  // 該類的實例變量大小
    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 該類的成員變量鏈表
    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定義的鏈表
    struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法緩存
    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 協(xié)議鏈表
#endif
} OBJC2_UNAVAILABLE;
```objc
typedef struct objc_class *Class;

struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};

當(dāng)創(chuàng)建一個特定類的實例對象時,分配的內(nèi)存包含一個objc_object數(shù)據(jù)結(jié)構(gòu),然后是類的實例變量的數(shù)據(jù)。

typedef struct objc_object *id;,id是一個objc_object結(jié)構(gòu)類型的指針。
可以看出block和id類型很相似。

三、 截獲自動變量值

1. 自動變量(局部變量)

Block語法表達(dá)式中使用自動變量被當(dāng)做成員變量追加到了__main_block_impl_0結(jié)構(gòu)體中。

struct __main_block_impl_0{
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    //下邊兩個即為
    int val;
    const char fmt;
}
  1. Block語法表達(dá)式中沒有使用的自動變量不會被追加,Blocks的自動變量截獲只針對Block中使用的自動變量。
  2. 結(jié)構(gòu)體內(nèi)聲明的成員變量類型與自動變量類型完全相同。(如:const那個仍為const)。

2. 可在block內(nèi)修改的情況

1.c語言中的變量類型:靜態(tài)局部變量、全局變量(包括靜態(tài)全局變量)
  1. 靜態(tài)局部變量,靜態(tài)變量的指針被追加到結(jié)構(gòu)體中:
int val;
struct __main_block_impl_0{
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    //下邊兩個即為
    int *val;
}
  1. 全局變量,不會被添加到結(jié)構(gòu)體中,因為在全局區(qū),使用的時候直接使用,與轉(zhuǎn)化前完全相同,直接修改。
2. 使用__block存儲域類說明符,__block說明符類似于static、auto說明符。

在自動變量前加__block :

__block int val = 10;
    void (^blk)(void) = ^{
        val = 3;
    };

轉(zhuǎn)化后的代碼為:

struct __Block_byref_val_0{
    void *isa;
    __Block_byref_val_0 *__forwarding;
    int __flags;
    int __size;
    int val;
};

struct __main_block_impl_0{
    struct __block_impl impl;
    struct __main_block_desc_0 *Desc;
    
    __Block_byref_val_0 *val;
};

我們可以看到,加上__block的自動變量竟然變成了結(jié)構(gòu)體實例,這個結(jié)構(gòu)體實例包含原val(val是該實例自身持有的變量,它相當(dāng)于原自動變量),而block結(jié)構(gòu)體實例中持有__block變量生成的結(jié)構(gòu)體實例指針。所以block結(jié)構(gòu)體中對__block變量的修改實際是對其指針的操作。

3. Block存儲域

通過之前可知,Block也是oc對象。將Block當(dāng)做oc對象來看時,該Block的類為_NSConcreteStackBlock/_NSConcreteMallocBlock/_NSConcreteGlobalBlock。

  • _NSConcreteStackBlock該類的實例對象Block設(shè)置在棧上。
  • _NSConcreteMallocBlock類的實例對象設(shè)置在堆上。
  • _NSConcreteGlobalBlock類的實例對象和全局變量一樣,設(shè)置在全局區(qū)(靜態(tài)區(qū))。

以下情況,Block為_NSConcreteGlobalBlock類的對象:

  • 全局變量的地方有Block語法。
  • Block語法的表達(dá)式中不使用截獲的自動變量時。

實際上當(dāng)ARC有效時,大多數(shù)情形下編譯器會恰當(dāng)?shù)剡M(jìn)行判斷,自動生成“將Block從棧上復(fù)制到堆上”的代碼。

1.以下情況,編譯器會自動將Block從棧上復(fù)制到堆上:

  • Block作為函數(shù)返回值時。
  • 向方法或函數(shù)的參數(shù)傳入Block時,并且在方法或函數(shù)中適當(dāng)?shù)膹?fù)制了Block。
  • Cocoa框架的方法名中含有usingBlock等時。
  • GCD的API。

2.以下情況,編譯器判斷不了,不會自動從棧復(fù)制到堆:

  • Block作為函數(shù)參數(shù)時,并且在方法中沒有復(fù)制Block。

例子:NSArray在使用enumerateObjectsUsingBlock方法時不用手動復(fù)制,相反的,在NSArray類的initWithObjects實例方法上傳遞Block時需要手動復(fù)制,因為此時編譯器不能判斷是否需要復(fù)制。

- (id)getBlockArray
{
    NSArray *array = [[NSArray alloc] initWithObjects:
                      [^{NSLog(@"block");} copy],
                      [^{NSLog(@"block");} copy],
                      nil];
    return array;
}

3.下面看一下,對這三種Block分別執(zhí)行copy的復(fù)制結(jié)果:

  • 棧上Block,copy,結(jié)果:從棧上復(fù)制到堆上。
  • 堆上Block,copy,結(jié)果:引用計數(shù)增加。
  • 全局Block,copy,結(jié)果:什么也不做。

不管在何處,用copy方法復(fù)制Block都不會產(chǎn)生任何問題。所以在不確定的時候調(diào)用copy即可。

4.隨意copy可以嗎?

但是,將Block從棧復(fù)制到堆上是相當(dāng)消耗cpu的,所以還是搞清楚什么棧上的才需要copy,避免cpu資源浪費。

那么堆上的Block多次調(diào)用copy會是什么結(jié)果呢?
如下代碼:

blk = [[[blk copy] copy] copy];

該代碼可解釋如下:

{
        blk_t tmp = [blk copy];
        blk = tmp;
    }
    {
        blk_t tmp = [blk copy];
        blk = tmp;
    }
    {
        blk_t tmp = [blk copy];
        blk = tmp;
    }

由此可以看出,ARC有效時完全沒有問題。

4. __block變量存儲域

  1. 使用__block變量的Block從棧上復(fù)制到堆上時,__block變量也會受到影響:
  • Block從棧復(fù)制到堆時,__block變量從棧復(fù)制到堆,此時Block持有(強(qiáng)引用)__block變量。
  • Block已經(jīng)在堆上時,復(fù)制Block,對__block變量不會產(chǎn)生任何影響。
  1. 當(dāng)一個__block變量,在多個Block中使用,這些Block從棧復(fù)制到堆上時,第一個Block被復(fù)制到堆上時,__block變量也會一并被復(fù)制到堆上,并被該Block持有(強(qiáng)引用),剩下的Block從棧復(fù)制到堆時,被復(fù)制的Block持有該__block變量,增加__block變量的引用計數(shù)。

  2. __block變量與oc引用計數(shù)式內(nèi)存管理完全相同。使用__block變量的Block持有__block變量。如果Block被廢棄,它所持有的__block變量也就被釋放。

  3. 前邊提到過使用__block修飾的變量,會被轉(zhuǎn)化為一個結(jié)構(gòu)體實例,該結(jié)構(gòu)體結(jié)構(gòu)如下:

struct __Block_byref_val_0{
    void *isa;
    __Block_byref_val_0 *__forwarding;
    int __flags;
    int __size;
    int val;
};

那么,如果__block變量跟隨Block一起被復(fù)制到堆上了,如果在Block外修改__block變量他們修改的是同一個值嗎?是怎么回事呢?原來棧上的__block變量結(jié)構(gòu)體呢?
上代碼:

__block int val = 0;
    void (^blk)(void) = [^{val++;} copy];
    val++;
    blk();

一步一步,分析,首先__block修飾的變量,會在棧上生成個結(jié)構(gòu)體實例,這個結(jié)構(gòu)體實例包含了原來的val變量。然后,Block執(zhí)行copy的時候,Block結(jié)構(gòu)體實例和__block結(jié)構(gòu)體實例一起又被復(fù)制到了堆上,Block語法表達(dá)式中使用的val是堆上的結(jié)構(gòu)體實例,Block語法外使用的val是棧上生成的那個結(jié)構(gòu)體實例。(注意關(guān)鍵點來了,__block變量的結(jié)構(gòu)體實例中有個__forwarding,正常情況下一直都是指向該結(jié)構(gòu)體自己的)當(dāng)__block變量的結(jié)構(gòu)體實例被復(fù)制到堆上的時候,之前棧上的那個__block變量結(jié)構(gòu)體實例的__forwarding指針會指向堆中新生成的__block變量的結(jié)構(gòu)體實例,堆中的指向它自己。
所以Block表達(dá)式中的val++ 和Block外val++,都是調(diào)用的

(val.__forwarding->val)++;

即堆中的那個__block變量生成的結(jié)構(gòu)體實例。

所以,無論是在Block語法中、語法外使用__block變量,還是__block配置在棧上或堆上,都可以順利訪問到同一個__block變量。

四、 截獲對象

前邊談的都是Block對普通C語言自動變量的截獲,下面來看看Block中使用oc對象時,是如何實現(xiàn)的。

    Model *model = [[Model alloc] init];
    model.name = @"lili";
    void(^blk)(void) = ^{
        NSLog(@"%@",model.name);
    };
    blk();

轉(zhuǎn)化后的代碼為:

struct __test1__test_block_impl_0 {
  struct __block_impl impl;
  struct __test1__test_block_desc_0* Desc;
  Model *model;
  __test1__test_block_impl_0(void *fp, struct __test1__test_block_desc_0 *desc, Model *_model, int flags=0) : model(_model) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static struct __test1__test_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __test1__test_block_impl_0*, struct __test1__test_block_impl_0*);
  void (*dispose)(struct __test1__test_block_impl_0*);
} __test1__test_block_desc_0_DATA = { 0, sizeof(struct __test1__test_block_impl_0), __test1__test_block_copy_0, __test1__test_block_dispose_0};

static void __test1__test_block_copy_0(struct __test1__test_block_impl_0*dst, struct __test1__test_block_impl_0*src) {_Block_object_assign((void*)&dst->model, (void*)src->model, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __test1__test_block_dispose_0(struct __test1__test_block_impl_0*src) {_Block_object_dispose((void*)src->model, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void _I_test1_test(test1 * self, SEL _cmd) {
    Model *model = ((Model *(*)(id, SEL))(void *)objc_msgSend)((id)((Model *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Model"), sel_registerName("alloc")), sel_registerName("init"));
    ((void (*)(id, SEL, NSString *))(void *)objc_msgSend)((id)model, sel_registerName("setName:"), (NSString *)&__NSConstantStringImpl__var_folders_6j_k3qjqfy94gl_jnc3llgm1nvr0000gn_T_test1_58f22c_mi_0);
    void(*blk)(void) = ((void (*)())&__test1__test_block_impl_0((void *)__test1__test_block_func_0, &__test1__test_block_desc_0_DATA, model, 570425344));
    ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
}
  1. 我們會發(fā)現(xiàn),Block生成的結(jié)構(gòu)體實例中多了個成員變量,為對象即指針,它與Block引用的原對象類型完全一致(包括所有權(quán)修飾符),指向?qū)ο笤诙阎猩傻膬?nèi)容。(我們在ARC的時候提到過,結(jié)構(gòu)體中不能含有oc對象,但是此處oc的運行時能夠準(zhǔn)確的把握Block從棧復(fù)制到堆上以及堆上Block被廢棄的時機(jī)。我的理解是我們寫進(jìn)去的,編譯器不知道怎么處理,此處是編譯器自己寫進(jìn)去的,它完全知道)
  2. copy函數(shù)和dispose函數(shù)
  • copy函數(shù)中的_Block_object_assign函數(shù)相當(dāng)于retain實例方法,將對象賦值在結(jié)構(gòu)體成員變量中的對象類型。
  • dispose函數(shù)中的_Block_object_dispose函數(shù)相當(dāng)于release實例方法,釋放賦值在結(jié)構(gòu)體成員變量中的對象類型。
  1. copy函數(shù)和dispose函數(shù)調(diào)用的時機(jī)
  • copy函數(shù):當(dāng)棧上的Block被復(fù)制到堆時,會生成新的Block結(jié)構(gòu)體實例,此時會調(diào)用copy函數(shù),將原對象賦值在結(jié)構(gòu)體成員變量中的對象類型。
  • dispose函數(shù):當(dāng)堆上的Block結(jié)構(gòu)體被廢棄時,會調(diào)用dispose函數(shù),釋放賦值在結(jié)構(gòu)體成員變量中的對象類型。

另外,堆上的Block結(jié)構(gòu)體都是通過從棧上復(fù)制過來的,也就是有堆上的結(jié)構(gòu)體之前一定先有棧上的結(jié)構(gòu)體。我們可以打印一下引用計數(shù):

Model *model = [[Model alloc] init];
    NSLog(@"retainCount:%d",_objc_rootRetainCount(model));
    model.name = @"lili";
    void(^blk)(void) = ^{
        NSLog(@"%@",model.name);
    };
    NSLog(@"retainCount:%d",_objc_rootRetainCount(model));
    blk();

打印出來結(jié)果為:

2017-09-06 14:07:54.002 BlockTest[42344:3847518] retainCount:1
2017-09-06 14:07:54.002 BlockTest[42344:3847518] retainCount:3

可以發(fā)現(xiàn)經(jīng)過Block之后,mode對象的引用計數(shù)增加了兩次,應(yīng)該棧上的Block結(jié)構(gòu)體引用一次,復(fù)制到堆上的時候堆上的結(jié)構(gòu)體又引用一次。棧上的結(jié)構(gòu)體引用時直接通過model對象指針賦值給Block結(jié)構(gòu)體成員變量中的對象類型。堆上的Block結(jié)構(gòu)體是通過copy方法來完成了成員變量的對象引用。
(最后這里只是我的個人理解,如果理解有問題,望留言指正。)

前邊說了,copy函數(shù)是棧上Block復(fù)制到堆上時執(zhí)行,那么問題來了。

  1. 什么時候棧上的Block會復(fù)制到堆呢?(劃重點)
  • 調(diào)用Block的copy實例方法時
  • Block作為函數(shù)返回值時
  • 將Block賦值給__strong修飾符的id類型或Block類型的成員變量時
  • 向方法名中含有usingBlock的Cocoa方法或GCD的API中傳遞Block時

所以,在Block中使用對象類型自動變量時,除了以上情形外,推薦調(diào)用Block的copy實例方法。

五、__block變量和對象

__block說明符可以指定任何類型的自動變量。下面就看一下指定oc對象類型。
看代碼:

__block id obj = [[NSObject alloc] init];

通過clang轉(zhuǎn)化如下:

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*);
 id obj;
};

static void _I_test11_test(test11 * self, SEL _cmd) {
    __attribute__((__blocks__(byref))) __Block_byref_obj_0 obj = {(void*)0,(__Block_byref_obj_0 *)&obj, 33554432, sizeof(__Block_byref_obj_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
}

可以看到,
1.通過__block修飾的對象類型也變成了一個結(jié)構(gòu)體實例(這與__block修飾的普通C語言自動變量一樣),原對象實例(直接想成指針更好理解)作為一個成員變量被包含于該結(jié)構(gòu)體實例中。
2.當(dāng)Block被復(fù)制到堆時,該__block結(jié)構(gòu)體實例也會被復(fù)制到堆中,堆上新建的__block變量的結(jié)構(gòu)體實例,通過copy函數(shù)(_Block_object_assign函數(shù))持有賦值給它的結(jié)構(gòu)體實例中的對象。當(dāng)堆上的__block變量被廢棄的時候,使用_Block_object_dispose釋放賦值給__block變量的對象。

  • 所以可以看出,在使用"對象所指向的內(nèi)容"時與不加__block修飾符時相同,即加不加__block效果都一樣(下邊有補(bǔ)充的更通俗的說法),加了以后只不過多了一層結(jié)構(gòu)體的包裝。
  • __weak和__unsafe_unretained時,加不加__block也一樣。
  • __autorelease 和__block一起時,會編譯錯誤。

補(bǔ)充,__block修飾oc對象的時候和__block修飾普通C語言自動變量的時候還有個區(qū)別,就是對象本身其實是指針,它其實包含兩個元素:
比如:

NSMutableArray *array = [[NSMutableArray alloc] init];
  1. 指針本身,即array。
  2. 還有一個就是指針指向的值,即生成的數(shù)組空間。
    所以,對于oc對象來說,
  • __block修飾之前,指針在棧上,值在堆上,所以指針指向不能修改,值可以修改,比如addObject增加內(nèi)容。
  • __block修飾之后,指針指向和值內(nèi)容,都在堆上,都可以修改了。

六、循環(huán)引用

如果Block中使用了__strong修飾符的對象類型自動變量,那么當(dāng)Block從棧復(fù)制到堆的時候,該對象被Block所持有。這樣容易引起循環(huán)引用。
可以使用__weak修飾對象,這樣Block不會持有對象。
也可以使用__block修飾符,比如:

__block Model *tmp = self;
    _myBlock = ^{
        NSLog(@"name:%@",tmp.name);
        tmp = nil;
    };
    _myBlock();

只要_myBlock()執(zhí)行以后,Block不再持有self,則不會有問題了。

Block與使用__weak的比較:

優(yōu)點:

  • 使用__block可以控制對象的持有期間。只要Block持有該對象,則該對象就不會釋放。不想使用的時候再釋放,可保證使用的時候該對象一定存在。
  • 在不想使用__weak的時候,不用不得已的去使用__unsafe_unretained修飾符(不必?fù)?dān)心懸垂指針,野指針),直接用__block就行。

缺點:

  • 必須執(zhí)行Block才能避免循環(huán)引用。

七、ARC無效時

  1. ARC無效時,一般需要手動將Block從棧復(fù)制到堆,由于ARC無效,所以肯定要釋放。這里我們使用copy實例方法來復(fù)制,使用release實例方法來釋放。
  2. Blocks是C語言的擴(kuò)展,所以在C語言中使用Block語法,此時使用Block_copy和Block_release函數(shù)來代替實例方法。
  3. __block可以在ARC無效時避免循環(huán)引用
    這是由于ARC無效時,當(dāng)Block從棧復(fù)制到堆時,若Block使用的變量為附有__block說明符的id類型或?qū)ο箢愋偷淖詣幼兞浚粫籸etain;若Block使用的變量為沒有__block說明符的id類型或?qū)ο箢愋偷淖詣幼兞?,則被retain。
    代碼如下:
__block Model *tmp = self;
    _myBlock = ^{
        NSLog(@"name:%@",tmp.name);
    };

可以看出,ARC下通過在Block中置空對象來實現(xiàn)避免循環(huán)引用;非ARC下僅加個修飾符即可。

注意:ARC有效和無效時,__block的作用差別很大,所以一定要弄清楚。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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