[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-2]

2.3 Blocks的实现

2.3.1 Block的实质
通过命令”clang -rewrite-objc 文件名”能够将含有Block语法的源代码转换为C++源代码。
含有Block的源代码如下:
#include <stdio.h>//不导入库文件无法运行
int main() {
   
void(^testBlock)(void)=^{
       
printf("i am testBlock");
    };
    testBlock();
}
转换后的代码有五百行左右,这里只选择我们关心的部分:
struct __block_impl {
 
void *isa;
 
int Flags;
 
int Reserved;
 
void *FuncPtr;
};
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("i am testBlock");
}

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() {
   
void(*testBlock)(void)=(void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA);
    ((
void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
}

通过Blocks使用的匿名函数被作为简单的C语言函数来处理:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        printf("i am testBlock");
}
该函数的参数”__cself”相当于C++实例方法中指向实例真身的变量this,也相当于OC示例方法中指向自身的变量”self”,即参数”__cself”为指向Block值的变量。

__cself是__main_block_impl_0类型的结构体指针,该结构体的定义是(不包括构造函数):
struct __main_block_impl_0 {
 
struct __block_impl impl;
  struct __main_block_desc_0* Desc;
}
可以看到,__main_block_impl_0共有两个变量,第一个是__block_impl类型的“impl”,定义如下
struct __block_impl {
 
void *isa;
 
int Flags;
 
int Reserved;
 
void *FuncPtr;
};
第二个是__main_block_desc_0类型的”Desc",定义如下:
static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
}
回头看一下__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(*testBlock)(void)=(void (*)())&__main_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 *testBlock=&tmp;
其中调用构造函数时有两个参数,第一个是由Block语法转换的C语言函数指针,第二个作为静态全局变量初始化的__main_block_desc_0结构体实例指针,初始化代码如下:
__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
可知该源码使用__main_desc_impl_0结构体实例的大小进行初始化。
下面看一下__main_desc_impl_0初始化的过程,展开__block_impl,其结构体与初始化对比如下:

结构体:
struct __main_block_impl_0 {
//__block_impl
  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;

使用Block的源码为:testBlock();
转换后的源码为:
((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
去掉转换部分之后:
(*testBlock->impl.FuncPtr)(testBlock);
这是使用函数指针调用函数。之前__main_block_func_0赋值给FuncPtr,而参数(__cself)就是指向Block的值。
文字的解释不够直观,可看图理解。
技术分享

接下来解释一下
isa = &_NSConcreteStackBlock;这句话。
将Block指针赋值给Block结构体成员变量isa。为了理解这句话,先要理解OC中类与对象的实质,下面是解释类与对象的关系.

Block是OC对象,“id”用来存储OC对象,id类型也能够在c语言中声明:
typedef struct objc_object{
Class isa;
} *id;
id 为objc_object结构体指针类型。Class的定义如下:
typedef struct objc_class*Class;
Class为objc_class结构体的指针类型,objc_class结构体在模拟器目录下/usr/include/objc/runtime.h(导入 #import <objc/runtime.h>点击可进入)中声明如下(除去无关宏定义):
struct objc_class {
    Class isa;
};
objc_class与结构体objc_object的定义相同。但是分别是在类与对象中使用的结构体,下面通过简单的OC类声明来验证一下。
@interface MyObject : NSObject
{
   
int val0;
   
int val1;
}
@end
基于objc_object结构体,该类的对象的结构体如下:
struct MyObject{
    Class isa;
   
int val0;
   
int val1;
};
类中的实例变量被包含在对象的结构体中,类生成对象意味着类生成该类的对象的各个结构体实例,通过成员变量isa保持该类的结构体实例指针(即isa为指向所属类的结构体实例的指针),如下图:
技术分享


各类的结构体就是基于objc_class结构体的class_t结构体,class_t结构体在objc4运行时库的声明如下:
struct class_t{
   
struct class_t *isa;
   
struct class_t *superclass;
    Cache cache;
   
IMP *vtable;
    uintptr_t data_NEVER_USE;
};
在OC中,各个类的结构体实例均生成并保持各个类的class_t结构体实例。该实例存有类的相关信息,包括持有声明的成员变量、方法的名称、方法的实现(函数指针)、属性即父类的指针,并被OC运行时库所使用。

回到刚才的代码,_NSConcreteStackBlock相当于class_t的结构体实例,将Block作为OC对象处理,关于该类的信息放置于_NSConcreteStackBlock中。所以说Block是OC对象。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。