iOS-Block源碼分析

前言

iOS-深入研究Block這篇文章結(jié)合實(shí)例介紹了Block的類型,循環(huán)引用等問題,接著我們通過Block的源碼分析一下,它的底層是怎么操作的?

1 通過Clang分析Block

Block通過Clang將會(huì)編譯成什么樣的結(jié)構(gòu)呢,它的invoke,isa,簽名的原理是什么,我們來研究下。

#include "stdio.h"

int main(){

//    __Block_byref_a_0 int a = 18;
    int a = 8;
    void(^block)(void) = ^{

//        a++;
        printf("ro_robert - %d",a);
    };
    
     block();
    return 0;
}

我們通過xcrun -sdk iphonesimulator clang -S -rewrite-objc -fobjc-arc -fobjc-runtime=ios-14.5 block.c命令執(zhí)行一個(gè)得到block.cpp文件.
我們打開block.cpp文件,找到main函數(shù),如下

int main(){
    int a = 8;
    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));

     ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    return 0;
}
static struct IMAGE_INFO { unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = { 0, 2 };

從上可以看出,我們要研究的 void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a));*是這行代碼。
我們經(jīng)過調(diào)整下,如下

 void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a));

void(*block)(void) 這是一個(gè)函數(shù)指針,
這里的__main_block_impl_0就是一個(gè)函數(shù)調(diào)用。

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

把這行也調(diào)下,如下所示

 block->FuncPtr(block);

我們先看下__main_block_impl_0這個(gè)是什么,經(jīng)過搜下,如下

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

這是一個(gè)結(jié)構(gòu)體,

 void(*block)(void) = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA, a));

就是這個(gè)結(jié)構(gòu)體的構(gòu)造函數(shù),傳了三個(gè)參數(shù),其中有一個(gè)我們傳的參數(shù)是a,這個(gè)__main_block_impl_0這個(gè)結(jié)構(gòu)體中也有一個(gè)變量a,他們有什么關(guān)系呢,我們來看下。
我們?cè)赽lock.c中把這個(gè)變量a去掉,看下是什么效果。

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;
  }
};

這個(gè)就是現(xiàn)在__main_block_impl_0結(jié)構(gòu)體,我們發(fā)現(xiàn)這里的沒有a這個(gè)變量了,它的構(gòu)造函數(shù)同樣也沒有a這個(gè)參數(shù)。
我們?cè)賮砜聪轮暗慕Y(jié)構(gòu)體的構(gòu)造函數(shù)

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

這里: a(_a) 是C++語法,默認(rèn)會(huì)對(duì)傳過來的參數(shù)a賦值,_a會(huì)傳給a,說白了就是賦值操作,block在底層會(huì)把變量捕獲進(jìn)來,變成自己的成員變量。
我們通過真實(shí)的App代碼,如下

- (void)viewDidLoad {
    [super viewDidLoad];
    NSObject *objc = [NSObject alloc];

    __block NSObject *objc1 = [NSObject alloc];
    void (^block1)(void) = ^{
        NSLog(@"ro_Block %@ ",objc1);
    };
    block1();
}

我們打開ViewController.cpp文件,找到__ViewController__viewDidLoad_block_impl_0這個(gè)結(jié)構(gòu)體,如下

struct __ViewController__viewDidLoad_block_impl_0 {
  struct __block_impl impl;
  struct __ViewController__viewDidLoad_block_desc_0* Desc;
  NSObject *__strong objc1;
  __ViewController__viewDidLoad_block_impl_0(void *fp, struct __ViewController__viewDidLoad_block_desc_0 *desc, NSObject *__strong _objc1, int flags=0) : objc1(_objc1) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

這里面有一個(gè)NSObject *__strong objc1;對(duì)象,這說明我們的Block在捕獲取變量時(shí)會(huì)生成相應(yīng)的成員變量。
在編譯價(jià)段,impl.isa = &_NSConcreteStackBlock;這里是一個(gè)stackBlock;。
之前我們介紹過,捕獲局部或者屬性變量,又不是弱引用,應(yīng)該是這個(gè)MallocBlock,為什么這里是StatckBlock,我們來看下。

在這里我們是在編譯時(shí)是StackBlock,程序還要經(jīng)過運(yùn)行時(shí),才會(huì)變成堆,是如何變成堆呢,需要我們?cè)傺芯肯隆?br> 這里有一個(gè)fp參數(shù),是第一個(gè)參數(shù),經(jīng)過查找,它是__ViewController__viewDidLoad_block_func_0,而它又是這個(gè)

static void __ViewController__viewDidLoad_block_func_0(struct __ViewController__viewDidLoad_block_impl_0 *__cself) {
  NSObject *__strong objc1 = __cself->objc1; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_r7_rnm6hs1x2jg8bqy7sjwsskx00000gn_T_ViewController_2b1512_mi_0,objc1);
    }

函數(shù),就是傳一個(gè)函數(shù)過去,之后進(jìn)行了FuncPtr的執(zhí)行,就是去執(zhí)行__ViewController__viewDidLoad_block_func_0這個(gè)函數(shù),就是運(yùn)行起來。
block函數(shù)式保存,如果不調(diào)用執(zhí)行,永遠(yuǎn)也沒可能執(zhí)行它的功能邏輯。
我們切換到block.c代碼中,為了防止干擾。

#include "stdio.h"

int main(){

//    __Block_byref_a_0 int a = 18;
    int a = 18;
    void(^block)(void) = ^{

//        a++;
        printf("ro_robert - %d",a);
//        printf("ro_robert");

    };
    
     block();
    return 0;
}

通過命令編成cpp,我們看下__main_block_func_0

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy
   printf("ro_robert - %d",a);

    }

這里csef就是傳過來的就是自己,
int a = __cself->a;這里的int a__cself->a,就是賦值操作,內(nèi)容相同,但是地址不同,說白就是值拷貝。
接下來,我們把int a加上__block看下效果,代碼如下

#include "stdio.h"

int main(){

//    __Block_byref_a_0 int a = 18;
    __block int a = 18;
    void(^block)(void) = ^{

        a++;
        printf("ro_robert - %d",a);
//        printf("ro_robert");

    };
    
     block();
    return 0;
}

再次編譯,我們?cè)俜治鱿耞_block做了什么操作?
我們看下__main_block_impl_0的結(jié)構(gòu)體

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

我們發(fā)現(xiàn)int a 變成 *__Block_byref_a_0 a;
我們?cè)傧耺ain函數(shù)

int main(){


    __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 0, sizeof(__Block_byref_a_0), 18};
    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));

     ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    return 0;
}

經(jīng)過整理,如下

int main(){


    __Block_byref_a_0 a = {(void*)0,
        (__Block_byref_a_0 *)&a,
        0,
        sizeof(__Block_byref_a_0),
        18};
    
    void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_a_0 *)&a, 570425344));

     ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    return 0;
}

這里__Block_byref_a_0結(jié)構(gòu)體初始化,* (__Block_byref_a_0 )&a,這里取a的地址。

struct __Block_byref_a_0 {
  void *__isa;
__Block_byref_a_0 *__forwarding;
 int __flags;
 int __size;
 int a;
};

這里是__Block_byref_a_0這個(gè)結(jié)構(gòu)體,__forwarding這里指向a的地址,在這個(gè)__main_block_impl_0結(jié)構(gòu)體中把__forwarding傳給了參數(shù)a,在__main_block_func_0這個(gè)函數(shù)中

 __Block_byref_a_0 *a = __cself->a;

這里的__cself->a;與外界的a是一樣的,指向同一塊內(nèi)存區(qū)域,說明的就是指針拷貝。
__block生成的__Block_byref_a_0這樣的結(jié)構(gòu)體,傳給的block的是指針地址,這就是為什么加了__block可以修改外部變量。

2 Block的簽名和copy過程

我們?cè)赽lock.cpp文件發(fā)現(xiàn)__main_block_copy_0,__main_block_dispose_0,__main_block_desc_0這些結(jié)構(gòu)體,他們是干什么用的呢,我們來分析下block的copy過程。
我們通過斷點(diǎn),分析下匯編流程,調(diào)試代碼如下

- (void)viewDidLoad {
    [super viewDidLoad];
    NSObject *objc = [NSObject alloc];

    NSObject *objc1 = [NSObject alloc];
    void (^block1)(void) = ^{
        NSLog(@"robert_Block %@ ",objc1);
    };
    block1();
    
}

我們?cè)赽lock打斷點(diǎn),調(diào)試,如下

1

這里調(diào)用了objc_retainBlock函數(shù),走進(jìn)這個(gè)函數(shù)(也可以通過符號(hào)斷點(diǎn)),發(fā)現(xiàn)調(diào)用了_Block_copy這個(gè)函數(shù),通過符號(hào)斷點(diǎn),是在libsystem_blocks.dylib中,這個(gè)是沒有開源的,我們找替找工程libclosure(這里用79版本),可以查看到,我們看下它的源碼,如下

void *_Block_copy(const void *arg) {
    struct Block_layout *aBlock;

    if (!arg) return NULL;
    
    // The following would be better done as a switch statement
    aBlock = (struct Block_layout *)arg;
    if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }
    else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }
    else {// 棧 - 堆 (編譯期)
        // Its a stack block.  Make a copy.
        size_t size = Block_size(aBlock);
        struct Block_layout *result = (struct Block_layout *)malloc(size);
        if (!result) return NULL;
        memmove(result, aBlock, size); // bitcopy first
#if __has_feature(ptrauth_calls)
        // Resign the invoke pointer as it uses address authentication.
        result->invoke = aBlock->invoke;

#if __has_feature(ptrauth_signed_block_descriptors)
        if (aBlock->flags & BLOCK_SMALL_DESCRIPTOR) {
            uintptr_t oldDesc = ptrauth_blend_discriminator(
                    &aBlock->descriptor,
                    _Block_descriptor_ptrauth_discriminator);
            uintptr_t newDesc = ptrauth_blend_discriminator(
                    &result->descriptor,
                    _Block_descriptor_ptrauth_discriminator);

            result->descriptor =
                    ptrauth_auth_and_resign(aBlock->descriptor,
                                            ptrauth_key_asda, oldDesc,
                                            ptrauth_key_asda, newDesc);
        }
#endif
#endif
        // reset refcount
        result->flags &= ~(BLOCK_REFCOUNT_MASK|BLOCK_DEALLOCATING);    // XXX not needed
        result->flags |= BLOCK_NEEDS_FREE | 2;  // logical refcount 1
        _Block_call_copy_helper(result, aBlock);
        // Set isa last so memory analysis tools see a fully-initialized object.
        result->isa = _NSConcreteMallocBlock;
        return result;
    }
}

我們知道Block是一個(gè)結(jié)構(gòu)體,struct Block_layout *aBlock;Block_layout類型的,它的源碼如下

struct Block_layout {
    void * __ptrauth_objc_isa_pointer isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};
  • 這里面有isa指針
  • flags標(biāo)識(shí)
  • invoke調(diào)用函數(shù)
  • descriptor其它相關(guān)描述,是否正在析構(gòu)等。
    這個(gè)flags標(biāo)識(shí)符的定義有
// Values for Block_layout->flags to describe block objects
enum {
    BLOCK_DEALLOCATING =      (0x0001),  // runtime 正在析構(gòu)
    BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime 掩碼
    BLOCK_INLINE_LAYOUT_STRING = (1 << 21), // compiler

#if BLOCK_SMALL_DESCRIPTOR_SUPPORTED
    BLOCK_SMALL_DESCRIPTOR =  (1 << 22), // compiler
#endif

    BLOCK_IS_NOESCAPE =       (1 << 23), // compiler
    BLOCK_NEEDS_FREE =        (1 << 24), // runtime
    BLOCK_HAS_COPY_DISPOSE =  (1 << 25), // compiler
    BLOCK_HAS_CTOR =          (1 << 26), // compiler: helpers have C++ code
    BLOCK_IS_GC =             (1 << 27), // runtime
    BLOCK_IS_GLOBAL =         (1 << 28), // compiler
    BLOCK_USE_STRET =         (1 << 29), // compiler: undefined if !BLOCK_HAS_SIGNATURE
    BLOCK_HAS_SIGNATURE  =    (1 << 30), // compiler 簽名
    BLOCK_HAS_EXTENDED_LAYOUT=(1 << 31)  // compiler
};

我們對(duì)_Block_copy符號(hào)斷點(diǎn),然后通命令查看,如圖

2

這是一個(gè)全局block。我們?cè)俑南麓a,讓這個(gè)block捕獲外部變量,如圖
3

這是一個(gè)stackBlock,這個(gè)block應(yīng)該是MallocBlock,因?yàn)檫@里還沒有經(jīng)過copy操作,當(dāng)我們執(zhí)行完copy操作后,如圖
4

這里成為了MallocBlock,我們?cè)倏瓷厦鎋Block_copy的源碼,

*aBlock = (struct Block_layout *)arg;*

這里轉(zhuǎn)換Block_layout類型。

 if (aBlock->flags & BLOCK_NEEDS_FREE) {
        // latches on high
        latching_incr_int(&aBlock->flags);
        return aBlock;
    }

這是是引用計(jì)數(shù)據(jù)相關(guān)處理。

else if (aBlock->flags & BLOCK_IS_GLOBAL) {
        return aBlock;
    }

如果是GlobalBlock直接返回。
經(jīng)過編譯期過來的是不能生成堆的,當(dāng)發(fā)現(xiàn)是一個(gè)stackBlock,又捕獲了外界變量,

size_t  size = Block_size(aBlock);
struct Block_layout *result = (struct Block_layout *)malloc(size);

這里就會(huì)根據(jù)原來的大小,就會(huì)開辟一段內(nèi)存空間,然后把原始的數(shù)據(jù)拷貝到新的Block_layout中,isa指針標(biāo)記為_NSConcreteMallocBlock,這里就變成了堆Block。

上圖中所示的invoke是函數(shù)調(diào)用者,signatrue就是Block的簽名。
signatrue的解釋 v8@?0 v代表返回值,8代表8字節(jié), @?代表Block類型,0代表從0號(hào)位置開始

3 Blocklayout的結(jié)構(gòu)

當(dāng)我們invoke的時(shí)候,這個(gè)消息會(huì)失效或有問題,會(huì)進(jìn)入消息轉(zhuǎn)發(fā)流程,在最后慢速轉(zhuǎn)發(fā)流程時(shí),必須要獲取簽名才能進(jìn)行invocation.
我再次看下blocklayout的結(jié)構(gòu)體

struct Block_layout {
    void * __ptrauth_objc_isa_pointer isa;
    volatile int32_t flags; // contains ref count
    int32_t reserved;
    BlockInvokeFunction invoke;
    struct Block_descriptor_1 *descriptor;
    // imported variables
};

我們看下descriptor這個(gè)類型,如下

#define BLOCK_DESCRIPTOR_1 1
struct Block_descriptor_1 {
    uintptr_t reserved; 
    uintptr_t size; // 大小
};

descriptor是可選參數(shù),內(nèi)存連續(xù)可選,因?yàn)槲覀兊念愋筒灰粯樱越Y(jié)構(gòu)也不一樣,因?yàn)橛衧tackBlock,MallocBlock,GlobalBlock的區(qū)分,每個(gè)類型的結(jié)構(gòu)體不一樣,通過標(biāo)識(shí)符判斷。

我們?cè)谠创a找到

#define BLOCK_DESCRIPTOR_2 1
struct Block_descriptor_2 {
    // requires BLOCK_HAS_COPY_DISPOSE
    BlockCopyFunction copy;
    BlockDisposeFunction dispose;
};

#define BLOCK_DESCRIPTOR_3 1
struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};

這些結(jié)構(gòu)體,這些都是通過標(biāo)識(shí)符號(hào)來判斷。

static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
{
    uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
    desc += sizeof(struct Block_descriptor_1);
    return (struct Block_descriptor_2 *)desc;
}

這里通過內(nèi)存平移來獲取到Block_descriptor_2,我們分析下。

static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
{
    uint8_t *desc = (uint8_t *)_Block_get_descriptor(aBlock);
    desc += sizeof(struct Block_descriptor_1);
    if (aBlock->flags & BLOCK_HAS_COPY_DISPOSE) {
        desc += sizeof(struct Block_descriptor_2);
    }
    return (struct Block_descriptor_3 *)desc;
}

這里也是通過平移,判斷有沒有Block_descriptor_2,有的話,再加上Block_descriptor_2的大小就是Block_descriptor_3的起始位置。

我們看下Block_descriptor_3的結(jié)構(gòu),如

struct Block_descriptor_3 {
    // requires BLOCK_HAS_SIGNATURE
    const char *signature;
    const char *layout;     // contents depend on BLOCK_HAS_EXTENDED_LAYOUT
};

這里有signature,只要有signature的打印,就說明肯定有Block_descriptor_3,我們可以通過x/6gx命令,查看內(nèi)存。

4 Block的捕獲變量生命周期

Block捕獲變量時(shí)都做了哪些操作,_Block_copy這個(gè)函數(shù)做了什么?帶著這些疑問,我們繼續(xù)分析底層原理。
我們先看下ViewController.cpp文件,在文件中搜索__ViewController__viewDidLoad_block_desc_0,這個(gè)函數(shù),源碼如下

static struct __ViewController__viewDidLoad_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __ViewController__viewDidLoad_block_impl_0*, struct __ViewController__viewDidLoad_block_impl_0*);
  void (*dispose)(struct __ViewController__viewDidLoad_block_impl_0*);
} __ViewController__viewDidLoad_block_desc_0_DATA = { 0, sizeof(struct __ViewController__viewDidLoad_block_impl_0), __ViewController__viewDidLoad_block_copy_0, __ViewController__viewDidLoad_block_dispose_0};

這里copy和dispose兩個(gè)函數(shù),__ViewController__viewDidLoad_block_dispose_0這個(gè)就是 Block_descriptor_2中的dispose,__ViewController__viewDidLoad_block_copy_0_Block_copy函數(shù)的指針,它做了什么事情呢,我們來看下,__ViewController__viewDidLoad_block_copy_0這個(gè)函數(shù)里面會(huì)調(diào)用_Block_object_assign這個(gè)函數(shù),相當(dāng)于copy函數(shù)。
我們繼續(xù)分析
_Block_object_assign,看看它的操作流程, 我們?cè)谠创a中搜下_Block_object_assign*這個(gè)函數(shù),如下

The flags parameter of _Block_object_assign and _Block_object_dispose is set to
    * BLOCK_FIELD_IS_OBJECT (3), for the case of an Objective-C Object, // 普通object對(duì)象,未使用__block修飾的
    * BLOCK_FIELD_IS_BLOCK (7), for the case of another Block, and
    * BLOCK_FIELD_IS_BYREF (8), for the case of a __block variable.對(duì)應(yīng)__block修飾
If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)

這里是三個(gè)標(biāo)識(shí)符號(hào),捕獲變量的判斷和相應(yīng)處理。我們看下_Block_object_assign函數(shù)的源碼,如

void _Block_object_assign(void *destArg, const void *object, const int flags) {
    const void **dest = (const void **)destArg;
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
      case BLOCK_FIELD_IS_OBJECT:
        /*******
        id object = ...;
        [^{ object; } copy];
        ********/

        // _Block_retain_object_default = fn (arc)
        _Block_retain_object(object);
        *dest = object;
        break;

      case BLOCK_FIELD_IS_BLOCK:
        /*******
        void (^object)(void) = ...;
        [^{ object; } copy];
        ********/
        
        *dest = _Block_copy(object);
        break;
    
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
        /*******
         // copy the onstack __block container to the heap
         // Note this __weak is old GC-weak/MRC-unretained.
         // ARC-style __weak is handled by the copy helper directly.
         __block ... x;
         __weak __block ... x;
         [^{ x; } copy];
         ********/

        *dest = _Block_byref_copy(object);
        break;
        
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
        /*******
         // copy the actual field held in the __block container
         // Note this is MRC unretained __block only. 
         // ARC retained __block is handled by the copy helper directly.
         __block id object;
         __block void (^object)(void);
         [^{ object; } copy];
         ********/

        *dest = object;
        break;

      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
        /*******
         // copy the actual field held in the __block container
         // Note this __weak is old GC-weak/MRC-unretained.
         // ARC-style __weak is handled by the copy helper directly.
         __weak __block id object;
         __weak __block void (^object)(void);
         [^{ object; } copy];
         ********/

        *dest = object;
        break;

      default:
        break;
    }
}

如果是BLOCK_FIELD_IS_OBJECT普通對(duì)象類型,執(zhí)行

 _Block_retain_object(object);
 *dest = object;

_Block_retain_object調(diào)用了_Block_retain_object_default,這個(gè)函數(shù)空實(shí)現(xiàn),默認(rèn)交給系統(tǒng)級(jí)別的ARC操作。
對(duì)象賬值給目標(biāo)dest,具備相同的內(nèi)存空間。
BLOCK_FIELD_IS_BLOCK Block類型,執(zhí)行

*dest = _Block_copy(object);

_Block_copy調(diào)用。
BLOCK_FIELD_IS_BYREF類型,執(zhí)行

 *dest = _Block_byref_copy(object);

_Block_byref_copy這個(gè)函數(shù)。我們看下它的源碼,如下

static struct Block_byref *_Block_byref_copy(const void *arg) {
    struct Block_byref *src = (struct Block_byref *)arg;

    //
    if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) {
        // src points to stack
        struct Block_byref *copy = (struct Block_byref *)malloc(src->size);
        copy->isa = NULL;
        // byref value 4 is logical refcount of 2: one for caller, one for stack
        copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4;
        copy->forwarding = copy; // patch heap copy to point to itself
        src->forwarding = copy;  // patch stack to point to heap copy
        copy->size = src->size;

        if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
            // Trust copy helper to copy everything of interest
            // If more than one field shows up in a byref block this is wrong XXX
            struct Block_byref_2 *src2 = (struct Block_byref_2 *)(src+1);
            struct Block_byref_2 *copy2 = (struct Block_byref_2 *)(copy+1);
            copy2->byref_keep = src2->byref_keep;
            copy2->byref_destroy = src2->byref_destroy;

            if (src->flags & BLOCK_BYREF_LAYOUT_EXTENDED) {
                struct Block_byref_3 *src3 = (struct Block_byref_3 *)(src2+1);
                struct Block_byref_3 *copy3 = (struct Block_byref_3*)(copy2+1);
                copy3->layout = src3->layout;
            }

            // 捕獲到了外界的變量 - 內(nèi)存處理 - 生命周期的保存
            (*src2->byref_keep)(copy, src);
        }
        else {
            // Bitwise copy.
            // This copy includes Block_byref_3, if any.
            memmove(copy+1, src+1, src->size - sizeof(*src));
        }
    }
    // already copied to heap
    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
        latching_incr_int(&src->forwarding->flags);
    }
    
    return src->forwarding;
}
  • if ((src->forwarding->flags & BLOCK_REFCOUNT_MASK) == 0) 引用計(jì)數(shù)相關(guān)的處理,
    arc自己找。
    struct Block_byref *copy = (struct Block_byref )malloc(src->size);這里copy一份
    copy->isa = NULL;為空。
    copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4; 標(biāo)識(shí)符賦值
    copy->forwarding = copy; forwarding拷貝一份
    src->forwarding = copy;copy賦給原始的forwarding,這說明原來的
    forwardingcopy后的forwarding是同一個(gè),都是棧到堆,forwarding傳給了objc1這個(gè)對(duì)象。
    if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE)這里判斷
    BLOCK_BYREF_HAS_COPY_DISPOSE*被捕獲的變量進(jìn)到這個(gè)判斷里面。
    接著執(zhí)行
copy2->byref_keep = src2->byref_keep;
copy2->byref_destroy = src2->byref_destroy;

原始對(duì)象與copy對(duì)象一樣。

(*src2->byref_keep)(copy, src);

這里byref_keep進(jìn)行調(diào)用,我們捕獲取外界的變量,對(duì)它進(jìn)行相關(guān)內(nèi)存的處理,賦值操作,我們看下byref_keep源碼

struct Block_byref_2 {
    // requires BLOCK_BYREF_HAS_COPY_DISPOSE
    BlockByrefKeepFunction byref_keep; 
    BlockByrefDestroyFunction byref_destroy;
};

對(duì)外界變量生命周期的保存,如果外界的變量變?yōu)閚il了,block內(nèi)部的變量也成為nil了。
byref_keep相當(dāng)于copy函數(shù),
byref_destroy相當(dāng)于dipose函數(shù)
最后調(diào)用的就是_Block_object_assign這個(gè)函數(shù),在ViewController.cpp中搜下這個(gè)函數(shù),找到如下代碼

static void __Block_byref_id_object_copy_131(void *dst, void *src) {
 _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}

dst + 40就是copy,我們內(nèi)部的變量,也是Block_byref類型,占用24字節(jié),Block_byref_2占用16字節(jié),剛好這里就是Block_byref_3這個(gè)結(jié)構(gòu)體。

struct Block_byref_3 {
    // requires BLOCK_BYREF_LAYOUT_EXTENDED
    const char *layout;
};

這是Block_byref_3的結(jié)構(gòu)體,這里相當(dāng)于傳的是object1這個(gè)對(duì)象。
__block修飾的變量流程

  • copy,從??截惖綏I?/strong>
  • block捕獲變量 _Block_byref類型
  • 對(duì)_Block_byref對(duì)象進(jìn)行一次copy操作
  • 針對(duì)_Block_byref里面修飾的object進(jìn)行copy操作
  • byref_keep調(diào)用這個(gè)函數(shù)把里面的對(duì)象保存一下
  • 釋放調(diào)用_Block_object_dispose這個(gè)函數(shù)
void _Block_object_dispose(const void *object, const int flags) {
    switch (os_assumes(flags & BLOCK_ALL_COPY_DISPOSE_FLAGS)) {
      case BLOCK_FIELD_IS_BYREF | BLOCK_FIELD_IS_WEAK:
      case BLOCK_FIELD_IS_BYREF:
        // get rid of the __block data structure held in a Block
        _Block_byref_release(object);
        break;
      case BLOCK_FIELD_IS_BLOCK:
        _Block_release(object);
        break;
      case BLOCK_FIELD_IS_OBJECT:
        _Block_release_object(object);
        break;
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_OBJECT | BLOCK_FIELD_IS_WEAK:
      case BLOCK_BYREF_CALLER | BLOCK_FIELD_IS_BLOCK  | BLOCK_FIELD_IS_WEAK:
        break;
      default:
        break;
    }
}

這里_Block_object_dispose函數(shù)的源碼

  • BLOCK_FIELD_IS_OBJECT普通對(duì)象,執(zhí)行
_Block_release_object(object);

調(diào)用_Block_release_object_default這個(gè)函數(shù),空實(shí)現(xiàn)

  • BLOCK_FIELD_IS_BLOCK如果是Block類型,執(zhí)行
_Block_release(object);

_Block_release的源碼

void _Block_release(const void *arg) {
    struct Block_layout *aBlock = (struct Block_layout *)arg;
    if (!aBlock) return;
    if (aBlock->flags & BLOCK_IS_GLOBAL) return;
    if (! (aBlock->flags & BLOCK_NEEDS_FREE)) return;

    if (latching_decr_int_should_deallocate(&aBlock->flags)) {
        _Block_call_dispose_helper(aBlock);
        _Block_destructInstance(aBlock);
        free(aBlock);
    }
}

_Block_destructInstance銷毀實(shí)例對(duì)象, free(aBlock);釋放block。

  • BLOCK_FIELD_IS_BYREF byref類型(被_block捕獲的類型),執(zhí)行
_Block_byref_release(object);

_Block_byref_release的源碼

static void _Block_byref_release(const void *arg) {
    struct Block_byref *byref = (struct Block_byref *)arg;

    // dereference the forwarding pointer since the compiler isn't doing this anymore (ever?)
    byref = byref->forwarding;
    
    if (byref->flags & BLOCK_BYREF_NEEDS_FREE) {
        int32_t refcount = byref->flags & BLOCK_REFCOUNT_MASK;
        os_assert(refcount);
        if (latching_decr_int_should_deallocate(&byref->flags)) {
            if (byref->flags & BLOCK_BYREF_HAS_COPY_DISPOSE) {
                struct Block_byref_2 *byref2 = (struct Block_byref_2 *)(byref+1);
                (*byref2->byref_destroy)(byref);
            }
            free(byref);
        }
    }
}

這里調(diào)用了byref_destroy這個(gè)函數(shù),free(byref);釋放byref這個(gè)變量,byref里面的對(duì)象也會(huì)釋放。

else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
latching_incr_int(&src->forwarding->flags);
}如果不是正常對(duì)象 latching_incr_int執(zhí)行這個(gè)函數(shù),自己處理。

總結(jié)

這篇文章我們通過源碼分析了Block的底層是如何工作的,它的copy流程,釋放流程,捕獲對(duì)象做了比較詳細(xì)的分析。通過這篇文章我們對(duì)Block的底層有了很深層次的認(rèn)識(shí),有疑問,歡迎大家隨時(shí)來交流學(xué)習(xí)。

?著作權(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)容

  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,884評(píng)論 0 23
  • 關(guān)于我的倉(cāng)庫(kù) 這篇文章是我為面試準(zhǔn)備的iOS基礎(chǔ)知識(shí)學(xué)習(xí)中的一篇 我將準(zhǔn)備面試中找到的所有學(xué)習(xí)資料,寫的Demo,...
    太陽騎士索拉爾閱讀 685評(píng)論 0 5
  • 前言 ??之前寫過一篇block的文章,參考的源碼是libclosure-38的,跟libclosure-67有所...
    WhiteZero閱讀 5,016評(píng)論 5 29
  • 什么是block? 首先,看一個(gè)極簡(jiǎn)的block: block編譯轉(zhuǎn)換結(jié)構(gòu) 對(duì)其執(zhí)行clang -rewrite-...
    開了那么閱讀 649評(píng)論 0 0
  • 1.什么是Block Block是帶有自動(dòng)變量(局部變量)的匿名函數(shù)。Block的內(nèi)部數(shù)據(jù)結(jié)構(gòu)如下: isa指針:...
    alvin_wang閱讀 448評(píng)論 0 0

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