Block

作者: 游城十代2dai | 来源:发表于2020-04-10 16:46 被阅读0次
    1. Block 的本质?
    • Block 本质上也是一个 OC 对象, 它的内部有 isa 指针, Block 内封装了函数调用以及环境的 OC 对象
    1. __block 的作用? 注意什么?
    • 作用是可以解决 block 内部不可以修改 auto 变量的问题, __block 不能修饰 static 和全局变量
    • 注意 __block 修饰的变量在栈上的时候不会对指向的对象产生强引用, 当 copy 的时候才会进行强引用, 当然也要看具体修饰符, 如果是 __weak __unsafe_unretained 修饰的就是弱引用, 其中 MRC 下 __block 不会 retain
    1. Block 的属性修饰符为什么是 copy?
    • 如果 block 没有进行 copy 操作, 就不会出现在堆上, 放在堆上, 就由开发者进行内存管理, 使用的时候小心循环引用
    1. Block 在修改 NSMutableArray 的时候需不需要添加 __block?
    • 不需要, 因为比如添加删除元素的时候是对 array 内部的数据结构进行操作, 并不影响 array 的这个变量
    • 尽量不要凡事就加__block, 因为__block 本质是生成一个 OC 对象, 导致整个数据结构变得复杂
    1. 在 Block 内部使用 __strong 目的是什么?
    • 目的是保证 Block 执行完之前被 weak 过的对象不会释放, 顺便骗过编译器警告的错误

    PS: 由于 Block 的内容不仅仅在编译时期, 还有运行时也有相应的系统管理, 当使用__weak __strong的时候编译源码要用这个命令

    $ xcrun -sdk iphoneos -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0 main.m -o main-arm64.cpp
    

    以下是我测试相关内容的代码, 依旧是创建 Command Line Tool , 通过对 Block 的 本质--捕捉对象--类型--内存--循环引用 测试 :

    #import <Foundation/Foundation.h>
    #import <objc/runtime.h>
    
    void _block_nature(void);
    void _block_capture(void);
    void _block_type(void);
    void _block_memory(void);
    void _block_circle_reference(void);
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            // insert code here...
    //        _block_nature();
    //        _block_capture();
    //        _block_type();
    //        _block_memory();
            _block_circle_reference();
        }
        return 0;
    }
    
    
    
    struct __block_impl {
      void *isa;
      int Flags;
      int Reserved;
      void *FuncPtr;
    };
    
    struct __Block_byref_age_0 {
     void *__isa;
        /**
         * 这个 forwarding 指针是指向这个结构体的地址, 因为后面再用到 age 的时候都是通过这个指针访问的
         * ___block_nature_block_impl_0 这个里面的 age 可能出现在栈上, 但是整体被 copy 过, 就会在堆上, 栈上的 age 访问堆中的 age 才可以
         */
     struct __Block_byref_age_0 *__forwarding;
     int __flags;
     int __size;
     int age;
    };
    
    struct ___block_nature_block_desc_0 {
      size_t reserved;
      size_t Block_size;
    };
    
    struct ___block_nature_block_impl_0 {
      struct __block_impl impl;
      struct ___block_nature_block_desc_0* Desc;
      struct __Block_byref_age_0 *age; // by ref
    };
    
    
    void _block_nature(void) {
        // Block 本质上也是一个 OC 对象, 它的内部有 isa 指针
        // Block 内封装了函数调用以及环境的 OC 对象
        __block int age = 10;
        void (^block)(int, int) = ^(int a, int b){
            NSLog(@"age --- %d", age);
        };
        
        age = 20;
        
        
        block(2, 5);
    //  实际调用的结构   ((void (*)(__block_impl *, int, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 2, 5);
    //  函数被赋值给 FuncPtr, 函数调用的时候就是在用 FuncPtr
    
        
        struct ___block_nature_block_impl_0 *block_imp = (__bridge struct ___block_nature_block_impl_0 *)block;
        
        NSLog(@"debug --- 请看 block_struct.png 文件");
    }
    
    /*
     * _block_nature 编译后的代码
     
     struct ___block_nature_block_impl_0 {
       struct __block_impl impl;
       struct ___block_nature_block_desc_0* Desc;
       __Block_byref_age_0 *age; // by ref
       ___block_nature_block_impl_0(void *fp, struct ___block_nature_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
         impl.isa = &_NSConcreteStackBlock;
         impl.Flags = flags;
         impl.FuncPtr = fp;
         Desc = desc;
       }
     };
     
     * Block 的结构体
     
     struct __block_impl {
       void *isa;
       int Flags;
       int Reserved;
       void *FuncPtr;
     };
    
     
     */
    
    int gVar1 = 10;
    static int gVar2 = 10;
    
    void _block_capture(void) {
        auto int lVar1 = 10;
        static int lVar2 = 10;
        
        void (^block)(void) = ^{
            NSLog(@"gVar1 = %d, gVar2 = %d, lVar1 = %d, lVar2 = %d", gVar1, gVar2, lVar1, lVar2);
        };
        
        gVar1 = 20;
        gVar2 = 20;
        lVar1 = 20;
        lVar2 = 20;
        
        block();
        
    }
    /**
     * 由下面的源码分析可得:
     * 1. block 对作用域内的变量或进行捕获, 而全局变量不会进行捕获
     * 2. OC 类内部的 block 如果包含 self, 会进行捕获, 因为 OC 的方法会被转换为 C 代码, 默认前两个参数为 self 和 SEL
     * - (void)test() {
     *  void  (^block)(void) = ^{
     *   NSLog(@"%@", self);
     *  }
     * }
     */
    
    /*
     * 全局的变量在编译的时候没有被 block 结构体捕获成为自己的成员变量
     int gVar1 = 10;
     static int gVar2 = 10;
    
     struct ___block_capture_block_impl_0 {
       struct __block_impl impl;
       struct ___block_capture_block_desc_0* Desc;
       
     // 原本的 auto 修饰的局部变量被 block 捕获成为自己的变量, 但是只有值
       int lVar1;
     // 原本的 static 修饰的全局变量被 block 捕获成为自己的变量, 捕获到的是变量的地址
       int *lVar2;
       ___block_capture_block_impl_0(void *fp, struct ___block_capture_block_desc_0 *desc, int _lVar1, int *_lVar2, int flags=0) : lVar1(_lVar1), lVar2(_lVar2) {
         impl.isa = &_NSConcreteStackBlock;
         impl.Flags = flags;
         impl.FuncPtr = fp;
         Desc = desc;
       }
     };
     
     // block 保存下来的函数方法
     static void ___block_capture_block_func_0(struct ___block_capture_block_impl_0 *__cself) {
       int lVar1 = __cself->lVar1; // bound by copy
       int *lVar2 = __cself->lVar2; // bound by copy
    
             NSLog((NSString *)&__NSConstantStringImpl__var_folders_z5_7pz8l4jj29d86h_7fg45h7580000gn_T_main_f4f131_mi_2, gVar1, gVar2, lVar1, (*lVar2));
     }
     
     // 我的 _block_capture 实现
     void _block_capture(void) {
         auto int lVar1 = 10;
         static int lVar2 = 10;
    
         void (*block)(void) = ((void (*)())&___block_capture_block_impl_0((void *)___block_capture_block_func_0, &___block_capture_block_desc_0_DATA, lVar1, &lVar2));
    
         gVar1 = 20;
         gVar2 = 20;
         lVar1 = 20;
         lVar2 = 20;
    
         ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
    
     }
     
     */
    
    
    void _block_type(void) {
        // Block 是 OC 对象, 也有 isa 那就可以调用 class 方法, 有以下三种类型, 但是一切以运行时为主
        void(^block1)(void) = ^ {
            
        };
        
        Class block1Class = [block1 class];
        NSLog(@"block1Class --- %@", block1Class); // __NSGlobalBlock__
        
        int lVar = 10;
        void(^block2)(void) = ^ {
            printf("%d", lVar);
        };
        
        Class block2Class = [block2 class];
        NSLog(@"block2Class --- %@", block2Class); // __NSMallocBlock__
    
        
        NSLog(@"block3Class --- %@", [^{ printf("%d", lVar); } class]); // __NSStackBlock__
        
        __block int lVar2 = 10;
        void(^block4)(void) = ^ {
            lVar2 = 20;
        };
        
        Class block4Class = [block4 class];
        NSLog(@"block4Class --- %@", block4Class); // __NSMallocBlock__
        // block1 可以看出凡是没有访问了 auto 的变量就是 __NSGlobalBlock__
        // block2 block4 其实也是 StackBlock, 只不过 ARC 内部做了其他操作, 简单说 copy 了就会变成 __NSMallocBlock__
        // 其中全局的被 copy 依旧是全局的, 只有在栈上的才会被放入堆中, 并且对相应的 auto 对象变量进行一次 retain 也就是强引用
        // 这就是为什么对象持有 block 要用 copy
        
        // xcrun -sdk iphoneos -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0 main.m -o main-arm64.cpp
    }
    
    
    /**
     * Block 的内存管理
     *
     */
    void _block_memory(void) {
        __block int zwy_age = 10;
        NSObject *zwy_objc = [[NSObject alloc] init];
        NSObject *zwy_objc2 = [[NSObject alloc] init];
        __weak NSObject *weak_objc2 = zwy_objc2;
        void (^zwy_block)(void) = ^ {
            [zwy_objc description];
            [weak_objc2 description];
            zwy_age = 20;
        };
        
        zwy_block();
        
        /**
         * 当 block 被拷贝到堆上时会对其捕获的变量进行一次操作如下:
         * _Block_object_assign 函数会对 __block (MRC 不会强引用 OC 对象) 和 __strong 的变量进行强引用, 对 __weak 的进行弱引用
         * 当 block 被废弃的时候也会对这些变量进行一次 dispose 操作
         */
        /*
         __Block_byref_zwy_age_2 *zwy_age; // __block 修饰的变量
         NSObject *__strong zwy_objc; // block 中的 oc 对象
         NSObject *__weak weak_objc2; // block 中的 __weak oc 对象
         
         static void ___block_memory_block_copy_0(struct ___block_memory_block_impl_0*dst, struct ___block_memory_block_impl_0*src) {
         _Block_object_assign((void*)&dst->zwy_objc, (void*)src->zwy_objc, 3);
         _Block_object_assign((void*)&dst->weak_objc2, (void*)src->weak_objc2, 3);
         _Block_object_assign((void*)&dst->zwy_age, (void*)src->zwy_age, 8);
         }
        */
    }
    
    typedef void(^Block)(void);
    
    @interface Person : NSObject
    
    @property (nonatomic, copy) Block block;
    @property (nonatomic, assign) int age;
    @property (nonatomic, copy) NSString *name;
    @end
    @implementation Person
    - (void)dealloc {
        NSLog(@"%s", __func__);
    }
    @end
    
    void _block_circle_reference(void) {
        { // 产生了循环引用, 即 per 强引用 block, block 强引用 per, 所以出了这个作用域 per 不会 dealloc
            Person *per = [[Person alloc] init];
            per.age = 10;
            per.block = ^{
                NSLog(@"%d", per.age);
            };
        }
        
        { // 解决办法 __weak __unsafe_unretained
            Person *per = [[Person alloc] init];
            per.age = 10;
            __weak Person *weak_per = per;
            per.block = ^{
                NSLog(@"%d", weak_per.age);
            };
        }
        
        { // 解决办法
            __block Person *per = [[Person alloc] init];
            per.age = 10;
            per.block = ^{
                NSLog(@"%d", per.age);
                per = nil; // 必须 nil
            };
            per.block(); // 必须调用
        }
        
        sleep(1);
    }
    

    相关文章

      网友评论

          本文标题:Block

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