美文网首页
Block 01 - 底层实现

Block 01 - 底层实现

作者: 石头89 | 来源:发表于2019-08-05 16:18 被阅读0次

    Block 01 - 底层实现

    Block 是一个封装了函数调用以及函数调用环境的 Objective-C 对象。
    使用 Block 可以实现闭包,它是对 C 语言的一种扩充功能,我们的代码块在底层被封装成了一个函数,函数指针是 Block 底层结构体的一个成员,Block 底层有对外部局部变量捕获的机制。

    Block 的底层实现

    // Block 底层实现的基础结构体
    struct __block_impl {
        void *isa;    // Block Class 对象的指针
        int Flags;    // 标志变量,在实现 Block 的内部操作时会用到
        int Reserved; // 保留字段
        var *FuncPrt; // 功能函数的指针
    };
    
    // Block 的底层实现,Block 本质是一个 Objective-C 对象
    //
    // 当 Block 在栈上时,不会对 auto 变量指向的对象、__block 对象进行强引用。
    struct __xxx_block_impl_0 {
        struct __block_impl impl;        // 底层实现的基础结构体
        struct __xxx_block_desc_0 *Desc; // Block 的描述信息
        // auto_ivars...         // 被捕获的局部 auto 变量
        // *block_byref_ivars... // 被捕获的局部 auto 变量(__block 对象)
        // *static_ivars...      // 被捕获的局部 static 变量
        // 全局变量不需要捕获,在功能函数中直接访问
    
        // Constructor
        //
        // *fp: 功能函数的指针
        // *desc:当前结构体占用内存的大小
        // _auto_vars: 外部的局部 auto 变量(值传递)
        // *_block_byref_ivars: 外部的局部 auto 变量(__block 对象,引用传递)
        // *_static_vars: 外部的局部 static 变量(引用传递)
        __xxx_block_impl_0(void *fp, struct __xxx_block_desc_0 *desc, 
                           _auto_vars..., *_block_byref_ivars..., *_static_vars..., 
                           int flags = 0) : 
                           auto_ivar0(_auto_var0)..., 
                           block_byref_ivar0(_block_byref_ivar0)...,
                           static_ivar0(_static_var0)... {
            // _NSConcreteGlobalBlock _NSConcreteStackBlock _NSConcreteMallocBlock   
            impl.isa = &_NSConcreteStackBlock; 
            impl.Flags = flags;
            impl.FuncPrt = fp;
            Desc = desc;
        }
    };
    
    // Block 的描述信息
    static struct __xxx_block_desc_0 {
        size_t reserved;   // 保留字段
        size_t Block_size; // Block 占用内存的大小
        void (*copy)(struct __xxx_block_impl_0*, struct __xxx_block_impl_0*); // 为 auto 变量指向的对象、__block 对象做内存管理
        void (*dispose)(struct __xxx_block_impl_0*); // 为 auto 变量指向的对象、__block 对象做内存管理
    } __xxx_block_desc_0_DATA = { 0, sizeof(struct __xxx_block_impl_0), 
                                  __xxx_block_copy_0, __xxx_block_dispose_0 };
    
    // 当 Block 被拷贝到堆上时:
    // 会调用 Block 内部的 copy 函数。
    // copy 函数内部会调用 _Block_object_assign 函数。
    // _Block_object_assign 函数会对 __block 对象进行强引用。
    // _Block_object_assign 函数会根据 auto 变量的修饰符(__strong, __weak, __unsafe_unretained)做出相应的操作,进行强引用(retain)或弱引用。                             
    static void __xxx_block_copy0(struct __xxx_block_impl_0 *dst, 
                                  struct __xxx_block_impl_0 *src) {
        _Block_object_assign((void*)&dst->auto_ivar0, (void*)src->auto_ivar0, 3/*BLOCK_FIELD_IS_OBJECT*/);
        _Block_object_assign((void*)&dst->block_byref_ivar0, (void*)src->block_byref_ivar0, 8/*BLOCK_FIELD_IS_BYREF*/);
    }
    
    // 当 Block 从堆上移除时:
    // 会调用 Block 内部的 dispose 函数。
    // dispose 函数内部会调用 _Block_object_dispose 函数。
    // _Block_object_dispose 函数会自动释放被强引用的对象(release)。
    static void __xxx_block_dispose(struct __xxx_block_impl_0 *src) {    
        _Block_object_dispose((void*)src->auto_ivar0, 3/*BLOCK_FIELD_IS_OBJECT*/);
        _Block_object_dispose((void*)src->block_byref_ivar0, 8/*BLOCK_FIELD_IS_BYREF*/);
    }
    
    // 功能函数的底层实现
    static void __xxx_block_func_0(struct __xxx_block_impl_0 *__cself, vars...) {
        // auto_ivar0 = __cself->auto_ivar0;                // bound by copy
        // *block_byref_ivar0 = __cself->block_byref_ivar0; // bound by ref
        // *static_ivar0 = __cself->static_ivar0;           // bound by copy
        // ...
        
        // do something...
    }
    

    Block 的底层原理

    Block 是一个封装了函数调用以及函数调用环境的 Objective-C 对象。

    • Block 底层通过结构体实现,内部也有一个 isa 指针。
    • Block 内部通过一个指针调用功能函数。
    • Block 会对外部局部变量进行捕获。

    Block 底层结构图

    Block 的变量捕获(Capture)

    为了保证 Block 内部能够正常访问外部的局部变量,Block 有变量捕获机制。


    auto 变量的捕获示意图:


    Block 的类型

    Block 有 3 种类型,可以通过 class 方法或 isa 指针查看具体类型,3 种类型都是 NSBlock 的子类。

    • NSGlobalBlock (_NSConcreteGlobalBlock)
      • NSGlobalBlock --> __NSGlobalBlock --> NSBlock --> NSObject
    • NSStackBlock (_NSConcreteStackBlock)
      • NSStackBlock --> __NSStackBlock --> NSBlock --> NSObject
    • NSMallocBlock (_NSConcreteMallocBlock)
      • NSMallocBlock --> __NSMallocBlock --> NSBlock --> NSObject

    括号前面的是运行时的 Block 类型,括号内是将 Objective-C 转换为 C++ 后的类型。
    Blocks 都派生自 NSBlock,NSBlock 的父类是 NSObject。

    Block 所在的内存区域

    Block 调用 copy 后的结果

    Block 的使用示例

    定义完 Block 直接调用:

    int sum = ^(int a, int b) {
        return a + b;
    }(1, 2);
    

    先定义 Block 再调用:

    int (^add)(int, int) = ^(int a, int b) {
        return a + b;
    };
    int sum = add(1, 2);
    

    函数接收一个 Block,函数内部调用 Block:

    void fetchData(void (^completion)(id result)) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^ {
            // do something..
            completion(@"hello~");
        });
    }
    
    fetchData(^(id result) {
        NSLog(@"result: %@", result);
    });
    

    与上面示例的不同之处在于为 Block 先定义一个类型:

    typedef void (^Completion)(id result);
    
    void fetchData(Completion completion) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^ {
            // do something...
            completion(@"hello~");
        });
    }
    
    fetchData(^(id result) {
        NSLog(@"result: %@", result);
    });
    

    将 Block 作为属性使用:

    @interface Player : NSObject
    @property (nonatomic, copy) void (^onStop)(void);
    - (void)stop;
    @end
    
    @implementation Player
    - (void)stop {
        self.onStop();
    }
    @end
    
    Player *player = [[Player alloc] init];
    player.onStop = ^ {
        NSLog(@"停止播放");
    };
    [player stop];
    

    相关文章

      网友评论

          本文标题:Block 01 - 底层实现

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