【OC】block的实现

本文详细探讨了Objective-C中Block的实现原理,包括Block的本质、Block语法转换的C++源代码、Block结构体、截获自动变量、__Block说明符以及Block的储存域。通过对Block的深入解析,阐述了Block如何捕获和处理自动变量,以及在栈和堆上的行为。
摘要由CSDN通过智能技术生成

block的本质

Block语法实际上是作为极普通的C语言代码来处理的。通过支持Block的编译器,含有Block语法的源代码转换为一般C语言编译器能够处理的源代码,并作为极为普通的C语言源代码被编译。

这只不过是概念的问题,在实际编译时无法转换成我们能够理解的源码,但clang具有转换成我们可读源代码的功能。通过“-rewrite-objc”选项就能将含有block语法的源代码转换为C++的源代码。说是C++,其实尽是使用了struct结构,其本质是C语言的源代码。

clang -rewrite-objc 源代码文件名

下面,我们转换Block语法。

int main(int argc, const char * argv[]) {
   
    @autoreleasepool {
   
        
        void (^blk)(void) = ^{
   printf("Block\n");};
        
        blk();
    }
    return 0;
}

该源代码通过clang可变换为以下形式:

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("Block\n");}

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 argc, const char * argv[]) {
   
    /* @autoreleasepool */ {
    __AtAutoreleasePool __autoreleasepool; 
        void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
    }
    return 0;
}
static struct IMAGE_INFO {
    unsigned version; unsigned flag; } _OBJC_IMAGE_INFO = {
    0, 2 };

下面,我们将源代码分成几个部分逐步理解。

1. 最初源代码中的Block语法。

^{
   printf("Block\n");};

变换后的源代码。

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
   
printf("Block\n");
}

如变换后的源代码所示,通过Blocks使用的匿名函数实际上被作为简单的C语言函数来处理。另外,根据Block语法所属的函数名和该Block语法在该函数出现的顺序值来给经clang变换的函数命名。

该函数参数__cself相当于C++实例方法中指向实例自身的变量this,或是Objective-C实例方法中指向对象自身的变量self,及参__cself为指向Block值的变量。

2. 参数声明。

struct __main_block_impl_0 {
   
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
}

我们先除去构造函数,__main_block_impl_0结构体其实非常简单。第一个成员变量是impl,其__block_impl结构体的声明如下。

struct __block_impl {
   
  void *isa;
  int Flags;
  int Reserved;
  void *FuncPtr;
};

我们从其名称中可以联想到某些标志、今后升级所需的区域及函数指针。这些会在后面详细说明。第二个成员变量是Desc指针,以下为其__main_block_desc_0结构体说明。

struct __main_block_desc_0 {
   
	unsigned long reserved;
	unsigned long Block_size;
};

这些也如同其成员名称所示,其结构体为今后版本升级所需的区域和Block的大小。

3. 初始化这些结构体的__main_block_impl_0结构体的构造函数。

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

我们先看看构造函数的调用。

void (*blk)(void) = (void(*)(void))&__mian_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);

我们去掉转换的部分,具体如下:

struct __main_block_impl_0 tmp = __main_block_impl_0(__main_block_func_0, &__main_block_desc_0_DATA);

struct __main_block_impl_0 *blk = &tmp;

这样就容易理解了。该源代码将__main_block_impl_0结构体类型的自动变量,即栈上生成的__main_block_impl_0结构体实例的指针,赋值给__main_blcok_impl_0结构体指针类型的变量blk。以下为这部分对应的最初源代码。

void (^blk)(void) = ^{
   printf("Block\n");};

将Block语法生成的Block赋给Block类型变量blk。它等同于将__main_block_impl_0结构体实例的指针赋给变量blk。该源代码中的Block就是__main_block_impl_0结构体类型的自动变量,即栈上生成的__main_block_impl_0结构体实例。

4. 下面是__main_block_impl_0结构体实例构造参数。

__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)

__main_block_func_0 由Block语法转换的C语言函数指针。
&__main_block_desc_0_DATA 作为静态全局变量初始化的__main_block_desc_0_DATA结构体实例指针。

以下为__main_block_desc_0结构体实例的初始化部分代码。

static struct __main_block_desc_0 __main_block_desc_0_DATA = {
    0, sizeof(struct __main_block_impl_0)};

由此可知,该源代码使用__main_block_impl_0结构体实例的大小进行初始化。

5. 栈上的__main_block_impl_0结构体实例根据参数初始化。

如果展开__main_block_impl_0结构体的__block_impl结构体,可记述为如下形式:

struct __mian_block_impl_0 {
   
	void *isa;
	int Flags;
	int Reserved;
	void *FuncPtr;
	struct __main_block_desc_0* Desc;
}

该结构体根据构造函数会像下面这样初始化。

	isa = &_NSConcreteStackBlock;
    Flags = 0;
    Reserved = 0;
    FuncPtr = __main_block_func_0;
    Desc = &__main_block_desc_0_DATA;

6. 使用Block的部分。

blk()

这部分可变换为以下代码:

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

去掉转换部分。

(*blk->impl.FuncPtr)(blk);

这就是简单的使用指针调用函数。

7. _NSConcerteStackBlock

isa = &_NSConcerteStackBlock;

将Block指针赋给Block的结构体成员变量isa。其实Block就是Objective-C对象。

id

"id"这一变量类型用于储存Objectivec-C对象。在Objective-C源码中虽然可以像使用void *类型那样随意使用id,但此id类型也能够在C语言中声明。

typedef struct objc_object {
   
	Class isa;
} *id;
id为objc_object结构体的指针类型。

Class

Class为objc_class结构体的指针类型。objc_class结构体声明如下:

struct objc_class {
   
	Class isa;
};

objc_object结构体和objc_class结构体归根结底是各个对象和类的实现中使用的最基本的结构体。

编写简单的类声明来确认。

@interface MyObject : NSObject
{
   
    int val10;
    int val11;
}
@end

基于objc_object结构体,该类的对象的结构体如下:

struct NSObject_IMPL {
   
	Class isa;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值