美文网首页
二十三、iOS底层原理-block底层原理

二十三、iOS底层原理-block底层原理

作者: Mjs | 来源:发表于2020-11-13 16:50 被阅读0次

    block分类

    1. __NSGlobalBlock__
        void(^globalBlock)(void) = ^ {
            NSLog(@"1111111");
        };
        NSLog(@"%@",globalBlock);
    .......................
    <__NSGlobalBlock__: 0x108fb70b8>
    

    声明一个block,没有对外界变量持有为 __NSGlobalBlock__

    • 位于全局区
    • 在Block内部不使用外部变量,或者只适用静态变量和全局变量。
    2. __NSMallocBlock__
        int a = 10;
        void (^ malloBlock)(void) = ^{
            NSLog(@"Cooci - %d",a);
        };
    
        NSLog(@"%@",malloBlock);
    .......................
    <__NSMallocBlock__: 0x6000010ec840>
    
    • 位于堆区
    • 在Block内部使用局部变量或者OC属性,并且赋值给强引用或者Copy修饰。
      当block对外界变量持有是,就会从 __NSStackBlock__copy成 __NSMallocBlock__,具体情况下文分析
    3. __NSStackBlock__
        int a = 10;
        void (^ malloBlock)(void) = ^{
            NSLog(@"Cooci - %d",a);
        };
    
        NSLog(@"%@",malloBlock);
    .......................
    <__NSStackBlock__: 0x7ffee6c473f8>
    

    当block出栈就会销毁的时候为 __NSStackBlock__,block为函数参数是也是 __NSStackBlock__

    • 位于栈区
    • 与MallocBlock一样,可以在Block内部使用局部变量或者OC属性,但是不能赋值给强引用或者Copy修饰。

    block的循环引用

    互相持有.png

    对象A持有对象B,对象B又持有对象A,相互持有,最终导致两个对象都不能释放。

    
    @interface ViewController ()
    @property(nonatomic,copy)void(^blockA)(void);
    @property (nonatomic, copy) NSString *name;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        // 循环引用
        self.name = @"AAA";
        self.blockA = ^{
            NSLog(@"%@",self.name);
        };
        self.blockA();
    }
    @end
    
    - (void)dealloc{
        NSLog(@"dealloc 来了");
    }
    

    在这里,self持有block,block里又对self进行了持有,导致循环引用,dealloc不会打印。

    循环引用的解决方案
    1. __weak__strong
        __weak typeof(self) weakSelf = self;
        self.blockA = ^{
            NSLog(@"%@",weakSelf.name);
        };
        self.blockA();
    

    原来是self->block->self,现在变成了self->block->weakSelf->self
    将block中的self交给weakSelf,weakSelf在block结束时就会释放,我们可以通过CFGetRetainCount((__bridge CFTypeRef)(self))打印当前引用计数,发现self引用计数并没有增加。
    但是只用 __weak还会存在问题

        __weak typeof(self) weakSelf = self;
        self.block = ^(void){
            // 时间 - 精力
            // self 的生命周期
            __strong __typeof(weakSelf)strongSelf = weakSelf; // 可以释放 when
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",strongSelf.name);
            });
        };
        self.block();
    

    当存在延时运行的时候,block结束时weakSelf还没调用就被释放了。self的生命周期没有得到保存。这个时候就需要__strong对其进行强引用,strongSelf是临时变量,在除了作用空间就会被释放掉。weak-strong 强弱共舞

    1. __block
        __block ViewController *vc = self;
        self.block = ^(void){
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",vc.name);
                vc = nil;
            });
        };
        self.block();
    

    我们除了__weak自动释放还可以通过__block手动释放,通过__block 修饰之后可以在block中修改值,通过手动的方式销毁,但这里有个问题,如果block没有调用的话,就不会释放,因为self被block捕获了,没有调用所以无法释放。

    3.修改通信模式
    因为当前self是被持有的,但我们可以通过通知、代理、传参的方式将self传入。

        self.block = ^(ViewController *vc){
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"%@",vc.name);
            });
        };
        self.block(self);
    

    当前self就会当做临时变量压栈进来。

    block编译

    1.block的基本实现
    新建一个文件,实现以下代码

    #include "stdio.h"
    
    int main(){
    
        __block int a = 11;
        void(^block)(void) = ^{
            a++;
            printf("LG_Cooci - %d",a);
        };
        
        // block();
        return 0;
    }
    

    通过xclang -arch x86_64 -rewrite-objc block.c将其编译成cpp文件

    
    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("aaaa");
        }
    
    
    int main(){
    
        void(*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
    
         ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
        return 0;
    }
    

    可以看到block被转换成了__main_block_impl_0结构体。通过构造方法,block函数被编译成函数,作为参数参入impl.FuncPtr = fp,后面通过block->FuncPtr(block)调用block。block作为参数传入当前函数中,和oc方法一样,作为隐藏参数,可以更好地操作block,可以对block对其进行操作。

    1. __block
      我们先传入一个普通的参数a
    #include "stdio.h"
    int main(){
        int a = 10;
        void(^block)(void) = ^{
            printf("aaaa-%d",a);
        };
         block();
        return 0;
    }
    

    clang编译一下

    
    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;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      int a = __cself->a; // bound by copy
    
            printf("aaaa-%d",a);
        }
    
    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 a = 10;
        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;
    }
    

    可以看到在编译时就在block中自动生成了相应的变量a,通过构造方法赋值。isa指向为_NSConcreteStackBlock。在方法中直接出去a的值来操作,但是由于是赋值拷贝,所以无法修改外部变量,会造成代码歧义。
    现在我们加入__block

    int main(){
        __block int a = 10;
        void(^block)(void) = ^{
            a++;
            printf("aaaa-%d",a);
        };
         block();
        return 0;
    }
    

    clang编译一下

    struct __Block_byref_a_0 {
      void *__isa;
    __Block_byref_a_0 *__forwarding;
     int __flags;
     int __size;
     int a;
    };
    
    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;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_a_0 *a = __cself->a; // bound by ref
    
            (a->__forwarding->a)++;
            printf("aaaa-%d",(a->__forwarding->a));
        }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 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(){
        __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {
    (void*)0,
    (__Block_byref_a_0 *)&a, 
    0,
     sizeof(__Block_byref_a_0), 
    10};
        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结构体

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

    将a封装成相应的对象,通过地址拷贝传递到block函数中去,对其进行操作。

    block底层原理

    block源码调试
    我们现在block声明的地方打上断点,然后Debug Workflow -> Alaways Show Diassembly查看汇编。

    block断点调试.png
    加他条件断点,可以看到libsystem_blocks.dylib _Block_copy
    我们就可以找到block源码进行查看
    我们通过源码可以看到所有的参数都来源于Block_layout
    struct Block_layout {
        void *isa;
        volatile int32_t flags; // contains ref count
        int32_t reserved;
        BlockInvokeFunction invoke;
        struct Block_descriptor_1 *descriptor; //
        // imported variables
    };
    

    flags作为标识码标识了当前的一些标志

    // Values for Block_layout->flags to describe block objects
    enum {
        BLOCK_DEALLOCATING =      (0x0001),  // runtime 是否正在析构
        BLOCK_REFCOUNT_MASK =     (0xfffe),  // runtime 引用计数的MASK
        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  是否为全局GLOBAL
        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_descriptor 除了Block_descriptor_1还有Block_descriptor_2Block_descriptor_3

    
    #define BLOCK_DESCRIPTOR_1 1
    struct Block_descriptor_1 {
        uintptr_t reserved;
        uintptr_t size;
    };
    
    // 可选
    #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
    };
    

    Block_descriptor_1是一直存在的,而Block_descriptor_2Block_descriptor_3就是通过flags来决定是否存在

    #if 0
    static struct Block_descriptor_1 * _Block_descriptor_1(struct Block_layout *aBlock)
    {
        return aBlock->descriptor;
    }
    #endif
    
    static struct Block_descriptor_2 * _Block_descriptor_2(struct Block_layout *aBlock)
    {
        if (! (aBlock->flags & BLOCK_HAS_COPY_DISPOSE)) return NULL;
        uint8_t *desc = (uint8_t *)aBlock->descriptor;
        desc += sizeof(struct Block_descriptor_1);
        return (struct Block_descriptor_2 *)desc;
    }
    
    static struct Block_descriptor_3 * _Block_descriptor_3(struct Block_layout *aBlock)
    {
        if (! (aBlock->flags & BLOCK_HAS_SIGNATURE)) return NULL;
        uint8_t *desc = (uint8_t *)aBlock->descriptor;
        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;
    }
    

    如果flags显示存在,就找到aBlock->descriptor 通过内存平移去找到Block_descriptor_2Block_descriptor_3

    通过读取内存查看当前block.png

    我们可以通过lldb register read查看x0地址变化,发现在_Block_copy __NSStackBlock__ 变为了__NSMallocBlock__

    // Copy, or bump refcount, of a block.  If really copying, call the copy helper if present.
    // 栈 -> 堆 研究拷贝
    void *_Block_copy(const void *arg) {
        struct Block_layout *aBlock;
    
        if (!arg) return NULL;
        
        // The following would be better done as a switch statement
        //拷贝当前的block防止对外层的影响
        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.
            struct Block_layout *result =
                (struct Block_layout *)malloc(aBlock->descriptor->size);
            if (!result) return NULL;
            //内存拷贝
            memmove(result, aBlock, aBlock->descriptor->size); // bitcopy first
    #if __has_feature(ptrauth_calls)
            // Resign the invoke pointer as it uses address authentication.
            result->invoke = aBlock->invoke;
    #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 是怎么对外界变量进行操作的
    int main(int argc, char * argv[]) {
        @autoreleasepool {
            __block NSString *a = [NSString stringWithFormat:@"aaa"];
            void(^block)(void) = ^{
                a = @"xxx";
                printf("aaaa-%@",a);
            };
             block();
            
        }
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
    
    

    clang -x objective-c -rewrite-objc -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk main.m

    
    struct __Block_byref_a_0 {
      void *__isa;
    __Block_byref_a_0 *__forwarding;
     int __flags;
     int __size;
     void (*__Block_byref_id_object_copy)(void*, void*);
     void (*__Block_byref_id_object_dispose)(void*);
     NSString *a;
    };
    
    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;
      }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
      __Block_byref_a_0 *a = __cself->a; // bound by ref
    
                (a->__forwarding->a) = (NSString *)&__NSConstantStringImpl__var_folders__2_948tyv6520110qy_phw9x4fw0000gn_T_main_076a95_mi_1;
                printf("aaaa-%@",(a->__forwarding->a));
            }
    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    
    static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->a, 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, char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            __attribute__((__blocks__(byref))) __Block_byref_a_0 a = {(void*)0,(__Block_byref_a_0 *)&a, 33554432, sizeof(__Block_byref_a_0), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSString * _Nonnull (*)(id, SEL, NSString * _Nonnull, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders__2_948tyv6520110qy_phw9x4fw0000gn_T_main_076a95_mi_0)};
            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 UIApplicationMain(argc, argv, __null, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("AppDelegate"), sel_registerName("class"))));
    }
    
    
    
    
    
    static __NSConstantStringImpl __NSConstantStringImpl__var_folders__2_948tyv6520110qy_phw9x4fw0000gn_T_main_076a95_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"aaa",3};
    static void __Block_byref_id_object_copy_131(void *dst, void *src) {
     _Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
    }
    static void __Block_byref_id_object_dispose_131(void *src) {
     _Block_object_dispose(*(void * *) ((char*)src + 40), 131);
    }
    static __NSConstantStringImpl __NSConstantStringImpl__var_folders__2_948tyv6520110qy_phw9x4fw0000gn_T_main_076a95_mi_1 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"xxx",3};
    
    
    

    这是生成的descriptor的,我们在descriptor_2可以看到

    static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->a, (void*)src->a, 8/*BLOCK_FIELD_IS_BYREF*/);}
    

    我们去查看_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];
            ********/
            // objc 指针地址 weakSelf (self)
                // arc 进行处理
            _Block_retain_object(object);
                // 持有
            *dest = object;
            break;
    
          case BLOCK_FIELD_IS_BLOCK:
            /*******
            void (^object)(void) = ...;
            [^{ object; } copy];
            ********/
                
                // block 被一个 block 捕获
    
            *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;
        }
    }
    

    通过flag分为以下几种类型

    enum {
        // see function implementation for a more complete description of these fields and combinations
        BLOCK_FIELD_IS_OBJECT   =  3,  // id, NSObject, __attribute__((NSObject)), block, ...
        BLOCK_FIELD_IS_BLOCK    =  7,  // a block variable
        BLOCK_FIELD_IS_BYREF    =  8,  // the on stack structure holding the __block variable
        BLOCK_FIELD_IS_WEAK     = 16,  // declared __weak, only used in byref copy helpers
        BLOCK_BYREF_CALLER      = 128, // called from __block (byref) copy/dispose support routines.
    };
    

    我们常用的为BLOCK_FIELD_IS_OBJECTBLOCK_FIELD_IS_BYREF

    • BLOCK_FIELD_IS_OBJECT时什么都没做,交给ARC进行处理。
    • BLOCK_FIELD_IS_BYREF
    static struct Block_byref *_Block_byref_copy(const void *arg) {
        
        // Block_byref  结构体
        // 保存一份
        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;
            
            // 问题 - block 内部 持有的 Block_byref 所持有的对象 同一个
            //拷贝的对象和原有的对象修改的是同一个内存地址
            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;
    }
    
    

    一开始就对当前参数进行拷贝,将拷贝的和原来的forwarding指向同一个地址,这里的byref_keep就是__Block_byref_id_object_copy_131

    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指向的就是__Block_byref_a_0种的a

    struct __Block_byref_a_0 {
      void *__isa;
    __Block_byref_a_0 *__forwarding;
     int __flags;
     int __size;
     void (*__Block_byref_id_object_copy)(void*, void*);
     void (*__Block_byref_id_object_dispose)(void*);
     NSString *a;
    };
    

    这只_Block_object_assign指向的就是BLOCK_FIELD_IS_OBJECT,对NSObject进行copy。

    1. 通过_Block_copy对block进行copy
    2. __block byref 对结构体进行拷贝 _Block_object_assign
    3. 对结构体中的对象进行_Block_object_assign

    如果参数是block会一层层拷贝下去

    相关文章

      网友评论

          本文标题:二十三、iOS底层原理-block底层原理

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