iOS中Block实现原理回顾

重新回顾了一下iOS内存管理一书的Block部分,为了加深一下自己对block的理解,这里将其实现总结到这篇博客。

准备工作
  1. xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc XXXX.m 使用该命令将OC写的代码改用C++来表示,所以接下来将使用此命令完成对block的探究
  2. new一个文件DemoClass
#import "DemoClass.h"
@interface DemoClass()
@end

@implementation DemoClass

-(void)demoFunction{
    int a = 10;
    void(^testBlock)(void) = ^{
        NSLog(@"%i",a);
    };
    
    testBlock();
}
@end
block实现

cd到DemoClass.m所在的文件夹,然后使用前面提及的命令可以得到如下代码(使用命令后会生成一个DemoClass.cpp文件,拖到最后可看到):


struct __DemoClass__demoFunction_block_impl_0 {
  struct __block_impl impl;
  struct __DemoClass__demoFunction_block_desc_0* Desc;
  int a;
  __DemoClass__demoFunction_block_impl_0(void *fp, struct __DemoClass__demoFunction_block_desc_0 *desc, int _a, int flags=0) : a(_a) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
static void __DemoClass__demoFunction_block_func_0(struct __DemoClass__demoFunction_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_w4_98ssf10122ddt716rmjjy0vw0000gn_T_DemoClass_cde937_mi_0,a);
    }

static struct __DemoClass__demoFunction_block_desc_0 {
  size_t reserved;
  size_t Block_size;
} __DemoClass__demoFunction_block_desc_0_DATA = { 0, sizeof(struct __DemoClass__demoFunction_block_impl_0)};

static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) {
    int a = 10;
    void(*testBlock)(void) = ((void (*)())&__DemoClass__demoFunction_block_impl_0((void *)__DemoClass__demoFunction_block_func_0, &__DemoClass__demoFunction_block_desc_0_DATA, a));

    ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
}

// @end
block声明的实现

感觉还挺长的。。。。,不扯这些没用的,直接进入分析(如果觉得看不习惯c的命名方式,可以将它们用自己习惯的方式重新书写),很明显OC中的demoFunction方法对应的就是static void _I_DemoClass_demoFunction(DemoClass * self, SEL _cmd) ,在demoFunction中定义了一个testBlock闭包对应过来就是这一行代码(以下使用testBlock闭包表示OC中的代码,testBlock变量表示C++代码)

void(*testBlock)(void) = ((void (*)())&__DemoClass__demoFunction_block_impl_0((void *)__DemoClass__demoFunction_block_func_0, &__DemoClass__demoFunction_block_desc_0_DATA, a));

这句话比较简单粗暴,就是把__DemoClass__demoFunction_block_impl_0初始化方法生成的结构体地址复制给testBlock这个函数变量。既然如此我们就转到代码

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

这是一个结构体__DemoClass__demoFunction_block_impl_0,它包含了构造方法,这个构造方法需要传入两个指针,加上一个int类型变量a,flags有缺省值。这里的a其实就是OC中block捕获的局部变量a。至于另外两个指针,第一个指针就是这一段代码的地址

static void __DemoClass__demoFunction_block_func_0(struct __DemoClass__demoFunction_block_impl_0 *__cself) {
  int a = __cself->a; // bound by copy

        NSLog((NSString *)&__NSConstantStringImpl__var_folders_w4_98ssf10122ddt716rmjjy0vw0000gn_T_DemoClass_ace8b6_mi_0,a);
    }

里面有一个NSLog函数,很明显就是testBlock闭包的代码实现部分,第二个指针就是定义好了的变量__DemoClass__demoFunction_block_desc_0_DATA地址,也就是说声明testBlock闭包的时候系统构造了一个testBlock指针,这个指针指向一个__DemoClass__demoFunction_block_impl_0的结构体,这个结构体里面存储了testBlock闭包的实现以及捕获到的临时变量和相关描述。

block调用的实现

block的调用对应的代码如下:

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

当调用testBlock闭包的时候testBlock指针强转成__block_impl指针(testBlock指针实际上是__DemoClass__demoFunction_block_impl_0指针,由于__block_impl结构体是该结构体的第一个成员所以他们的首地址是一样的因此可以强行转换),然后通过FuncPtr获得testBlock这个闭包的实现,然后传入testBlock指针(主要是为了获取testBlock闭包捕获到的变量的值)。从而完成函数调用。
看着其实还是挺简单的。。。。。。

总结

OC中的闭包实际上对应着结构体__block_impl,由于这个结构体包含了isa指针因此可以相信的是闭包就是一个类,当你定义一个闭包的时候系统生成一个结构体,该结构体中包含了闭包的实现地址以及捕获的变量,需要调用闭包的时候,调用结构体中实现地址中的实现来完成调用。而且可以从实现中看到一个很关键的点,在OC中的局部变量a,是通过值传递的方式传递到结构体的,因此闭包中一般是无法修改捕获的局部变量的。
顺便提一下OC中class和object的结构体:

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;
    const char *name                                         OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

typedef struct objc_class *Class;

/// Represents an instance of a class.
struct objc_object {
    Class isa  OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值