美文网首页
iOS-Block源码分析

iOS-Block源码分析

作者: 似水流年_9ebe | 来源:发表于2021-08-24 21:35 被阅读0次

    前言

    iOS-深入研究Block这篇文章结合实例介绍了Block的类型,循环引用等问题,接着我们通过Block的源码分析一下,它的底层是怎么操作的?

    1 通过Clang分析Block

    Block通过Clang将会编译成什么样的结构呢,它的invokeisa,签名的原理是什么,我们来研究下。

    #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命令执行一个得到block.cpp文件.
    我们打开block.cpp文件,找到main函数,如下

    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));*是这行代码。
    我们经过调整下,如下

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

    void(*block)(void) 这是一个函数指针,
    这里的__main_block_impl_0就是一个函数调用。

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

    把这行也调下,如下所示

     block->FuncPtr(block);
    

    我们先看下__main_block_impl_0这个是什么,经过搜下,如下

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

    这是一个结构体,

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

    就是这个结构体的构造函数,传了三个参数,其中有一个我们传的参数是a,这个__main_block_impl_0这个结构体中也有一个变量a,他们有什么关系呢,我们来看下。
    我们在block.c中把这个变量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;
      }
    };
    

    这个就是现在__main_block_impl_0结构体,我们发现这里的没有a这个变量了,它的构造函数同样也没有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;
      }
    

    这里: a(_a) 是C++语法,默认会对传过来的参数a赋值,_a会传给a,说白了就是赋值操作,block在底层会把变量捕获进来,变成自己的成员变量。
    我们通过真实的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这个结构体,如下

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

    这里面有一个NSObject *__strong objc1;对象,这说明我们的Block在捕获取变量时会生成相应的成员变量。
    在编译价段,impl.isa = &_NSConcreteStackBlock;这里是一个stackBlock;。
    之前我们介绍过,捕获局部或者属性变量,又不是弱引用,应该是这个MallocBlock,为什么这里是StatckBlock,我们来看下。

    在这里我们是在编译时是StackBlock,程序还要经过运行时,才会变成堆,是如何变成堆呢,需要我们再研究下。
    这里有一个fp参数,是第一个参数,经过查找,它是__ViewController__viewDidLoad_block_func_0,而它又是这个

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

    函数,就是传一个函数过去,之后进行了FuncPtr的执行,就是去执行__ViewController__viewDidLoad_block_func_0这个函数,就是运行起来。
    block函数式保存,如果不调用执行,永远也没可能执行它的功能逻辑。
    我们切换到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,就是赋值操作,内容相同,但是地址不同,说白就是值拷贝。
    接下来,我们把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;
    }
    

    再次编译,我们再分析下__block做了什么操作?
    我们看下__main_block_impl_0的结构体

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

    我们发现int a 变成 *__Block_byref_a_0 a;
    我们再下main函数

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

    经过整理,如下

    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结构体初始化,* (__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这个结构体,__forwarding这里指向a的地址,在这个__main_block_impl_0结构体中把__forwarding传给了参数a,在__main_block_func_0这个函数中

     __Block_byref_a_0 *a = __cself->a;
    

    这里的__cself->a;与外界的a是一样的,指向同一块内存区域,说明的就是指针拷贝。
    __block生成的__Block_byref_a_0这样的结构体,传给的block的是指针地址,这就是为什么加了__block可以修改外部变量。

    2 Block的签名和copy过程

    我们在block.cpp文件发现__main_block_copy_0__main_block_dispose_0__main_block_desc_0这些结构体,他们是干什么用的呢,我们来分析下block的copy过程。
    我们通过断点,分析下汇编流程,调试代码如下

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

    我们在block打断点,调试,如下

    1
    这里调用了objc_retainBlock函数,走进这个函数(也可以通过符号断点),发现调用了_Block_copy这个函数,通过符号断点,是在libsystem_blocks.dylib中,这个是没有开源的,我们找替找工程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是一个结构体,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标识
    • invoke调用函数
    • descriptor其它相关描述,是否正在析构等。
      这个flags标识符的定义有
    // Values for Block_layout->flags to describe block objects
    enum {
        BLOCK_DEALLOCATING =      (0x0001),  // runtime 正在析构
        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
    };
    

    我们对_Block_copy符号断点,然后通命令查看,如图

    2
    这是一个全局block。我们再改下代码,让这个block捕获外部变量,如图
    3
    这是一个stackBlock,这个block应该是MallocBlock,因为这里还没有经过copy操作,当我们执行完copy操作后,如图
    4
    这里成为了MallocBlock,我们再看上面_Block_copy的源码,
    *aBlock = (struct Block_layout *)arg;*
    

    这里转换Block_layout类型。

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

    如果是GlobalBlock直接返回。
    经过编译期过来的是不能生成堆的,当发现是一个stackBlock,又捕获了外界变量,

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

    这里就会根据原来的大小,就会开辟一段内存空间,然后把原始的数据拷贝到新的Block_layout中,isa指针标记为_NSConcreteMallocBlock,这里就变成了堆Block。

    上图中所示的invoke是函数调用者,signatrue就是Block的签名。
    signatrue的解释 v8@?0 v代表返回值,8代表8字节, @?代表Block类型,0代表从0号位置开始

    3 Blocklayout的结构

    当我们invoke的时候,这个消息会失效或有问题,会进入消息转发流程,在最后慢速转发流程时,必须要获取签名才能进行invocation.
    我再次看下blocklayout的结构体

    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这个类型,如下

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

    descriptor是可选参数,内存连续可选,因为我们的类型不一样,所以结构也不一样,因为有stackBlock,MallocBlock,GlobalBlock的区分,每个类型的结构体不一样,通过标识符判断。

    我们在源码找到

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

    这些结构体,这些都是通过标识符号来判断。

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

    这里通过内存平移来获取到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的结构,如

    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命令,查看内存。

    4 Block的捕获变量生命周期

    Block捕获变量时都做了哪些操作,_Block_copy这个函数做了什么?带着这些疑问,我们继续分析底层原理。
    我们先看下ViewController.cpp文件,在文件中搜索__ViewController__viewDidLoad_block_desc_0,这个函数,源码如下

    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两个函数,__ViewController__viewDidLoad_block_dispose_0这个就是 Block_descriptor_2中的dispose__ViewController__viewDidLoad_block_copy_0_Block_copy函数的指针,它做了什么事情呢,我们来看下,__ViewController__viewDidLoad_block_copy_0这个函数里面会调用_Block_object_assign这个函数,相当于copy函数。
    我们继续分析
    _Block_object_assign,看看它的操作流程, 我们在源码中搜下_Block_object_assign*这个函数,如下

    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对象,未使用__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.对应__block修饰
    If the __block variable is marked weak the compiler also or's in BLOCK_FIELD_IS_WEAK (16)
    

    这里是三个标识符号,捕获变量的判断和相应处理。我们看下_Block_object_assign函数的源码,如

    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普通对象类型,执行

     _Block_retain_object(object);
     *dest = object;
    

    _Block_retain_object调用了_Block_retain_object_default,这个函数空实现,默认交给系统级别的ARC操作。
    对象账值给目标dest,具备相同的内存空间。
    BLOCK_FIELD_IS_BLOCK Block类型,执行

    *dest = _Block_copy(object);
    

    _Block_copy调用。
    BLOCK_FIELD_IS_BYREF类型,执行

     *dest = _Block_byref_copy(object);
    

    _Block_byref_copy这个函数。我们看下它的源码,如下

    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;
                }
    
                // 捕获到了外界的变量 - 内存处理 - 生命周期的保存
                (*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) 引用计数相关的处理,
      arc自己找。
      struct Block_byref *copy = (struct Block_byref )malloc(src->size);这里copy一份
      copy->isa = NULL;为空。
      copy->flags = src->flags | BLOCK_BYREF_NEEDS_FREE | 4; 标识符赋值
      copy->forwarding = copy; forwarding拷贝一份
      src->forwarding = copy;copy赋给原始的forwarding,这说明原来的
      forwardingcopy后的forwarding是同一个,都是栈到堆,forwarding传给了objc1这个对象。
      if (src->flags & BLOCK_BYREF_HAS_COPY_DISPOSE)这里判断
      BLOCK_BYREF_HAS_COPY_DISPOSE*被捕获的变量进到这个判断里面。
      接着执行
    copy2->byref_keep = src2->byref_keep;
    copy2->byref_destroy = src2->byref_destroy;
    

    原始对象与copy对象一样。

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

    这里byref_keep进行调用,我们捕获取外界的变量,对它进行相关内存的处理,赋值操作,我们看下byref_keep源码

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

    对外界变量生命周期的保存,如果外界的变量变为nil了,block内部的变量也成为nil了。
    byref_keep相当于copy函数,
    byref_destroy相当于dipose函数
    最后调用的就是_Block_object_assign这个函数,在ViewController.cpp中搜下这个函数,找到如下代码

    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,我们内部的变量,也是Block_byref类型,占用24字节,Block_byref_2占用16字节,刚好这里就是Block_byref_3这个结构体

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

    这是Block_byref_3的结构体,这里相当于传的是object1这个对象。
    __block修饰的变量流程

    • copy,从栈拷贝到栈上
    • block捕获变量 _Block_byref类型
    • 对_Block_byref对象进行一次copy操作
    • 针对_Block_byref里面修饰的object进行copy操作
    • byref_keep调用这个函数把里面的对象保存一下
    • 释放调用_Block_object_dispose这个函数
    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函数的源码

    • BLOCK_FIELD_IS_OBJECT普通对象,执行
    _Block_release_object(object);
    

    调用_Block_release_object_default这个函数,空实现

    • BLOCK_FIELD_IS_BLOCK如果是Block类型,执行
    _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销毁实例对象, free(aBlock);释放block。

    • BLOCK_FIELD_IS_BYREF byref类型(被_block捕获的类型),执行
    _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);
            }
        }
    }
    
    

    这里调用了byref_destroy这个函数,free(byref);释放byref这个变量,byref里面的对象也会释放。

    else if ((src->forwarding->flags & BLOCK_BYREF_NEEDS_FREE) == BLOCK_BYREF_NEEDS_FREE) {
    latching_incr_int(&src->forwarding->flags);
    }如果不是正常对象 latching_incr_int执行这个函数,自己处理。

    总结

    这篇文章我们通过源码分析了Block的底层是如何工作的,它的copy流程,释放流程,捕获对象做了比较详细的分析。通过这篇文章我们对Block的底层有了很深层次的认识,有疑问,欢迎大家随时来交流学习。

    相关文章

      网友评论

          本文标题:iOS-Block源码分析

          本文链接:https://www.haomeiwen.com/subject/hphriltx.html