<<iOS 與OS X多線程和內(nèi)存管理>>筆記:Blocks實(shí)現(xiàn)(二)


前言


<<iOS 與OS X多線程和內(nèi)存管理>>筆記:Blocks中我寫的都是我們?nèi)粘i_(kāi)發(fā)過(guò)程中所用到的Blocks.這里我們深層次的看一下Blocks的相關(guān)實(shí)現(xiàn).


把OC代碼轉(zhuǎn)換為C++結(jié)構(gòu)體代碼


為了使我們更方便看清Block內(nèi)部的運(yùn)行,我們需要把OC代碼代碼轉(zhuǎn)化為帶有結(jié)構(gòu)體的C++代碼.這里我們就需要使用到clang -rewrite-objc指令.步驟有如下兩步.

  • 打開(kāi)終端,使用cd指令進(jìn)入需要轉(zhuǎn)化的文件目錄下,比如我要對(duì)桌面上的Test工程下的main.m文件進(jìn)行轉(zhuǎn)化.終端指令類似于下圖所示.
  • 然后執(zhí)行如下的終端命令 clang -rewrite-objc main.m,如下所示.

然后在當(dāng)前文件夾下就會(huì)出現(xiàn)后綴為.cpp的C++執(zhí)行文件.如下所示.


Block的實(shí)現(xiàn)

首先,我們?cè)趍ain函數(shù)中寫一個(gè)簡(jiǎn)單block匿名函數(shù)并且進(jìn)行調(diào)用,如下所示.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        void (^blk)(void) = ^{printf("Block\n");};
        blk();
    }
    return 0;
}

然后,我們通過(guò) clang -rewrite-objc main.m指令把mian.m轉(zhuǎn)變?yōu)镃++文件.里面代碼較多,我們下拉到文件的最底部.

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}

我們可以看到,我們寫的block已經(jīng)被轉(zhuǎn)化為一個(gè)C++語(yǔ)言的函數(shù),如下所示.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
printf("Block\n");
}

概念函數(shù)的參數(shù)__cself相當(dāng)于C++實(shí)例方法中指向?qū)嵗陨淼淖兞縯his,或是Objective-C實(shí)例方法中指向?qū)ο笞陨淼淖兞縮elf,也就是說(shuō)參數(shù)____cself為指向Block值的變量.可是我們發(fā)現(xiàn)____cself并沒(méi)有在這里使用,這里我們先不做研究,我們先看一下參數(shù)____cself的本質(zhì).

struct __main_block_impl_0 *__cself


  • Block的結(jié)構(gòu)體

我們看到參數(shù)____cself是__main_block_impl_0 結(jié)構(gòu)體的指針,該結(jié)構(gòu)體如下所示.

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
    
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }   
};

通過(guò)<<iOS 與OS X多線程和內(nèi)存管理>>我們可以了解到兩個(gè)成員變量各包含什么信息.


  • Block結(jié)構(gòu)體的成員變量

我們先看一下成員變量impl的結(jié)構(gòu)體(在.cpp文件的頂部位置).如下所示.

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;//今后版本升級(jí)所需的區(qū)域
  void *FuncPtr;//函數(shù)指針
};

第二個(gè)成員變量Desc主要是存儲(chǔ)今后版本升級(jí)所需的區(qū)域和Block大小.具體如下所示.

static struct __main_block_desc_0 {
  size_t reserved; //今后版本升級(jí)所需的區(qū)域
  size_t Block_size; //Block大小
}


  • Block的構(gòu)造

接下來(lái)我們就看一下__main_block_impl_0的構(gòu)造函數(shù)是如何構(gòu)造的.在main函數(shù)中調(diào)用的源碼如圖所示.

書中為了方便大家理解這句代碼調(diào)用,進(jìn)行了如下的轉(zhuǎn)換.也就是說(shuō)blk其實(shí)上是指向類型為_(kāi)_main_block_impl_0的tmp結(jié)構(gòu)體指針.

        struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);

        struct __main_block_impl_0 *blk = &tmp;

接下來(lái)我們看一下結(jié)構(gòu)體的構(gòu)造函數(shù)的參數(shù).首先是__main_block_desc_0_DATA這個(gè)參數(shù).我們?cè)诖a中找到了它的賦值過(guò)程.如下所示.

static struct __main_block_desc_0  __main_block_desc_0_DATA = { 
                             0, 
                             sizeof(struct __main_block_impl_0)
};

通過(guò)上面的構(gòu)造函數(shù),__main_block_impl_0的值就會(huì)如下所示.

    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = 0;
    impl.Reserved = 0;
    impl.FuncPtr = ___main_block_func_0;
    Desc = &__main_block_desc_0_DATA;


  • Block的調(diào)用過(guò)程

接下來(lái)我們看一下使用block的代碼是如何實(shí)現(xiàn)的.

     blk();

找到.cpp文件對(duì)應(yīng)的代碼如下所示.

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

我們?nèi)サ艮D(zhuǎn)化部分.簡(jiǎn)化代碼之后如下所示.這句代碼是什么意思呢?這就是使用函數(shù)的指針調(diào)用函數(shù).正如我們剛剛所示的一樣.正如上一個(gè)模塊所說(shuō)的那樣,___main_block_func_0的函數(shù)指針被賦值到了結(jié)構(gòu)體的FuncPtr中了.另外___main_block_func_0的所需參數(shù)是__main_block_impl_0的類型,也就是blk.所以有以下的函數(shù)調(diào)用.

    (*blk->FuncPtr)(blk);


  • Block的實(shí)質(zhì)

這時(shí)候我們需要回過(guò)頭來(lái)說(shuō)明__main_block_impl_0結(jié)構(gòu)體成員變量 impl中的isa指針.

我們知道isa指針在構(gòu)造函數(shù)中被賦值為&_NSConcreteStackBlock.如下圖所示.

其實(shí)Block就是Objective-C對(duì)象.為什么這么說(shuō)呢?首先我們看一下什么叫做Objective-C對(duì)象.

在Objective-C中,任何類的定義都是對(duì)象。類和類的實(shí)例(對(duì)象)沒(méi)有任何本質(zhì)上的區(qū)別。任何對(duì)象都有isa指針。

假定我們創(chuàng)建一個(gè)如下的對(duì)象.

@interface MyObject : NSObject
{
    int val0;
    int val1;
}
@end

那么基于Objective-C對(duì)象的結(jié)構(gòu)體就應(yīng)該如下所示.

struct MyObject
{
    Class isa;
    int val0;
    int val1;
}

其中的isa指針指向如下所示.具體可查看書中的98頁(yè).


通過(guò)比較我們知道Block的結(jié)構(gòu)體中有isa指針._NSConcreteStackBlock就相當(dāng)于上圖的class_t結(jié)構(gòu)體實(shí)例.也就是說(shuō)Block即為Objective-C的對(duì)象.


Block截獲自動(dòng)變量值的實(shí)現(xiàn)


對(duì)于Block截獲自動(dòng)變量值,在<<iOS 與OS X多線程和內(nèi)存管理>>筆記:Blocks中我們已經(jīng)說(shuō)過(guò)了,現(xiàn)在我們列舉一下例子.來(lái)看一下是如何實(shí)現(xiàn)截獲自動(dòng)變量值這一過(guò)程的.

        int number = 1;
        
        void (^blk)(void) = ^{
            printf("value:%d\n",number);
        };
        number = 3;
        blk();

運(yùn)行程序.打印結(jié)果如下所示.


通過(guò)clang -rewrite-objc main.m指令編譯成C++文件.其中核心代碼如下所示.

struct __block_impl {
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int number;//新增成員變量
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

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

            printf("value:%d\n",number);
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};

int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        int number = 1;

        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, number));
        number = 3;
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);

    }
    return 0;
}

這時(shí)候我們把Block的結(jié)構(gòu)體拿出來(lái)看一下.我們發(fā)現(xiàn)新增了一個(gè)成員變量number以及構(gòu)造方法發(fā)生新增了對(duì)number的賦值.

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int number;//新增成員變量

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _number, int flags=0) : number(_number) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

然后看一下main函數(shù)中__main_block_impl_0構(gòu)造函數(shù)的構(gòu)造過(guò)程.

        int number = 1;
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, number));

這一步我們就知道在__main_block_impl_0結(jié)構(gòu)體構(gòu)造的時(shí)候已經(jīng)把number的值存儲(chǔ)到了自身成員變量number中了,所以后面number如何改變,那么Block在構(gòu)造完成之后打印的number值就不會(huì)發(fā)生改變了.

通過(guò)上面的表述,我們可以就了解為什么在不能Block中直接修改變量的值?(面試題).例如下圖所示.

這是為什么呢?我們看一下__main_block_func_0函數(shù)的實(shí)現(xiàn),如下所示.我們可以知道傳遞的是__main_block_impl_0結(jié)構(gòu)體的成員變量的值.而不是指針(其實(shí)就算是指針也沒(méi)有任何的關(guān)系),跟原來(lái)的number變量無(wú)任何關(guān)系.所以我們不能在函數(shù)中直接修改number變量變量.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            int number = __cself->number; // bound by copy

            printf("value:%d\n",number);
}


__block說(shuō)明符的實(shí)現(xiàn)


上面一個(gè)模塊最后我們說(shuō)到如果直接在block中給變量賦值會(huì)報(bào)錯(cuò),我們發(fā)現(xiàn)根本原因就是Block結(jié)構(gòu)體中傳遞的是變量值,而不是指針,那么如何解決這一問(wèn)題呢?這時(shí)候__block說(shuō)明符就出現(xiàn)了.我們看一下C語(yǔ)言代碼,如下所示.

        __block int number = 1;
        
        void (^blk)(void) = ^{
            printf("value:%d\n",number);
            number = 6;
        };
        blk();

但是通過(guò)clang -rewrite-objc main.m指令轉(zhuǎn)變的C++代碼去發(fā)生了很大的變化.核心代碼如下所示.

//numbr變量已經(jīng)通過(guò)__block的修飾變成了結(jié)構(gòu)體
struct __Block_byref_number_0 {
  void *__isa;
__Block_byref_number_0 *__forwarding;
 int __flags;
 int __size;
 int number;
};

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_number_0 *number; // by ref
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  __Block_byref_number_0 *number = __cself->number; // bound by ref

            printf("value:%d\n",(number->__forwarding->number));
            (number->__forwarding->number) = 6;
        }
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->number, (void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->number, 8/*BLOCK_FIELD_IS_BYREF*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 

        __attribute__((__blocks__(byref))) __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 1};

        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_number_0 *)&number, 570425344));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}

我們看一下主要改變的部分.int number = 1;變成__block int number = 1;之后,C++代碼如下所示.代碼量提升了不是一倍兩倍呀~

struct __Block_byref_number_0 {
  void *__isa;
__Block_byref_number_0 *__forwarding;//指向自身的指針
 int __flags;
 int __size;
 int number;
};

然后我們看一下在main函數(shù)中的構(gòu)造代碼.如下所示.

__attribute__((__blocks__(byref)))  __Block_byref_number_0 number = {(void*)0,(__Block_byref_number_0 *)&number, 0, sizeof(__Block_byref_number_0), 1};

簡(jiǎn)化代碼之后,如下所示.

__Block_byref_number_0 number = {
0,
&number,
0, 
sizeof(__Block_byref_number_0), 
1
};

這時(shí)候Block結(jié)構(gòu)體的構(gòu)造函數(shù)和新增成員變量也發(fā)生了改變.成員變量變成了指向__Block_byref_number_0類型的結(jié)構(gòu)體.

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __Block_byref_number_0 *number; //新增成員變量

  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_number_0 *_number, int flags=0) : number(_number->__forwarding) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

那么在block中進(jìn)行賦值的時(shí)候是如何操作的呢?這主要是通過(guò)__Block_byref_number_0的成員變量__forwarding來(lái)完成的.__forwarding是指向本身的指針.我們可以通過(guò)__forwarding來(lái)找到成員變量number的值.所以在__main_block_func_0函數(shù)實(shí)現(xiàn)中有如下的代碼.

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {

            __Block_byref_number_0 *number = __cself->number; // bound by ref
            printf("value:%d\n",(number->__forwarding->number));
            (number->__forwarding->number) = 6;
}

對(duì)于__Block_byref_number_0結(jié)構(gòu)體中的__forwarding指針,我們可以看下面的示意圖.


Block存儲(chǔ)域


通過(guò)下面一張表我們了解到Block和__block變量時(shí)存儲(chǔ)在棧區(qū)的結(jié)構(gòu)體類型自動(dòng)變量(一般情況下).

名稱 實(shí)質(zhì)
Block 棧上Block的結(jié)構(gòu)體實(shí)例
__block 棧上__block變量的結(jié)構(gòu)體實(shí)例

接下來(lái)我們還是來(lái)研究Block結(jié)構(gòu)體的isa指針,在前面的例子中,isa指針是指向_NSConcreteStackBlock的.其實(shí)還有很多類似的類.我們先用一張表格來(lái)說(shuō)明每個(gè)類的不同點(diǎn)

設(shè)置對(duì)象的存儲(chǔ)域 副本源的配置存儲(chǔ)域 復(fù)制效果
_NSConcreteStackBlock 從棧區(qū)復(fù)制到堆區(qū)
_NSConcreteMallocBlock 引用計(jì)數(shù)增加
_NSConcreteGlobaBlock 全局區(qū) 全局區(qū) 什么也不做

通過(guò)上面的表格,我們就可以知道兩個(gè)面試題的答案,

問(wèn): Block的類一共有幾種?
答: 三種,分別是 _NSConcreteStackBlock 、_NSConcreteMallocBlock、_NSConcreteGlobaBlock

問(wèn): Block為什么用copy修飾?
答: block在定義成屬性的時(shí)候應(yīng)該使用copy修飾,平常我們使用的block主要是存放在棧區(qū)的(有的也會(huì)存放在全局區(qū)).棧區(qū)的block出了作用域之后就會(huì)被釋放掉,如果我們?cè)赽lock釋放掉之后還繼續(xù)調(diào)用,那么就會(huì)出現(xiàn)crash.理論上,在全局區(qū)的block我們是不需要進(jìn)行copy的.但是大部分的block是存儲(chǔ)在棧區(qū)的,為了統(tǒng)一規(guī)范管理,所以我們都使用copy對(duì)block屬性進(jìn)行修飾.


__block變量存儲(chǔ)域


上一個(gè)模塊是對(duì)Block進(jìn)行了說(shuō)明,那么對(duì)于使用__block變量的Block從棧上復(fù)制到堆上是,__block變量會(huì)有什么影響呢?

__block變量的配置存儲(chǔ)域 Block從棧區(qū)復(fù)制到堆時(shí)的影響
從棧復(fù)制到堆并被Block持有
被Block持有

上面這張表是表達(dá)了什么意思呢? 也就是說(shuō):

  1. 如果有一個(gè)Block使用某個(gè)__block變量,那么__block變量會(huì)從棧復(fù)制到堆并被Block持有.
  2. 如果有多個(gè)Block使用某個(gè)__block變量,那么在第一個(gè)Block中__block變量會(huì)從棧復(fù)制到堆并被第一個(gè)Block持有.從第二個(gè)Block時(shí)是持有__block變量,也就是只會(huì)增加__block變量的引用計(jì)數(shù).

對(duì)于__forwarding指針(指向自身的指針),我們?cè)?jīng)說(shuō)過(guò),"不管__block變量配置在棧上還是堆上,都能正確訪問(wèn)該變量."我們可以通過(guò)下面的例子來(lái)說(shuō)明一下情況.

__block int val = 0;

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

++val;

blk();

NSLog(@"%d",val);

通過(guò)blk這個(gè)Block的copy操作, 被__block修飾的val變量成功的從棧上復(fù)制到了堆上了.

所以^{ ++val; }++val;都可以被轉(zhuǎn)化為以下的形式.

++(val.__forwarding->val);

我們可以通過(guò)下面的示意圖來(lái)表示上面的轉(zhuǎn)變過(guò)程.


截獲對(duì)象的實(shí)現(xiàn)


我們?cè)?jīng)說(shuō)過(guò)截獲變量值,現(xiàn)在我們說(shuō)一下截獲對(duì)象的實(shí)現(xiàn).演示源碼如下所示.

        void (^blk)(id obj);

        {//array的作用域
        id array = [[NSMutableArray alloc] init];
        blk = [^(id obj){
            
            [array addObject:obj];
            NSLog(@"array count = %ld",[array count]);
        } copy];
        }//array的作用域已經(jīng)結(jié)束

        blk([NSObject new]);
        blk([NSObject new]);
        blk([NSObject new]);

我們知道array的作用域已經(jīng)結(jié)束了(到達(dá)注釋位置時(shí)候),可以我們調(diào)用block仍然可以訪問(wèn)到array.如下所示,這是為什么呢?

實(shí)際上在blk的實(shí)現(xiàn)過(guò)程中.已經(jīng)持有了array對(duì)象.<<iOS 與OS X多線程和內(nèi)存管理>>是有以下代碼的.

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id  __strong array; //強(qiáng)引用的array成員變量
};

在Objective-C中,C語(yǔ)言結(jié)構(gòu)體并不能含有__strong修飾符的變量.因?yàn)榫幾g器不知道應(yīng)該何時(shí)進(jìn)行C語(yǔ)言結(jié)構(gòu)體的初始化和廢棄操作.不能很好的管理內(nèi)存.Objective-C的運(yùn)行時(shí)庫(kù)可以很好的把握Block從棧上復(fù)制到堆以及堆上的Block被廢棄的時(shí)機(jī).從而有效管理成員變量的持有和釋放.為此,在__main_block_desc_0就增建了兩個(gè)成員變量copy和dispose,已經(jīng)對(duì)應(yīng)的函數(shù).用于成員變量的持有和釋放.如下圖所示.

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->array, (void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->array, 3/*BLOCK_FIELD_IS_OBJECT*/);}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
  void (*dispose)(struct __main_block_impl_0*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};

可是我在實(shí)際過(guò)程中并沒(méi)有__strong修飾詞.個(gè)人猜想是已經(jīng)進(jìn)行了缺省操作了.省略了__strong的修飾符.源碼截圖如下所示.大家可以自行試驗(yàn)操作.


循環(huán)引用的本質(zhì)


上一個(gè)模塊我們說(shuō)了.Block可以持有對(duì)象.如果一個(gè)對(duì)象中含有某個(gè)Block的成員屬性(strong修飾).在Block中直接使用self,會(huì)造成循環(huán)引用,原因就出現(xiàn)__main_block_impl_0結(jié)構(gòu)體中的obj.__main_block_impl_0對(duì)obj是強(qiáng)引用,self對(duì)Block變量是強(qiáng)引用,兩者相互引用,最終造成循環(huán)引用.

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id  __strong obj; //強(qiáng)引用的obj成員變量
};

示意圖如下所示.



結(jié)束


這一篇Block的實(shí)現(xiàn)總共寫了三天,加上自己驗(yàn)證,收獲良多,希望這一篇博客對(duì)大家有所幫助.還是希望大家來(lái)看一下<<iOS 與OS X多線程和內(nèi)存管理>>原書,自己敲一遍實(shí)現(xiàn)源碼,這樣幫助很大,會(huì)加深印象.最后感謝各位看官查看本篇文章.如果有任何問(wèn)題,歡迎聯(lián)系騷棟.歡迎指導(dǎo)批斗.

<<iOS 與OS X多線程和內(nèi)存管理>>的PDF版?zhèn)魉烷T??



最后編輯于
?著作權(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)容