看 Aspects 源码的时候,发现了一段代码,当时不理解这个代码是什么意思,因此想要探究 block 的本质。以下是一点查阅资料并且自己测试后的一点见解。
typedef struct _AspectBlock {
__unused Class isa;
AspectBlockFlags flags;
__unused int reserved;
void (__unused *invoke)(struct _AspectBlock *block, ...);
struct {
unsigned long int reserved;
unsigned long int size;
// requires AspectBlockFlagsHasCopyDisposeHelpers
void (*copy)(void *dst, const void *src);
void (*dispose)(const void *);
// requires AspectBlockFlagsHasSignature
const char *signature;
const char *layout;
} *descriptor;
// imported variables
} *AspectBlockRef;
// ......
static NSMethodSignature *aspect_blockMethodSignature(id block, NSError **error) {
AspectBlockRef layout = (__bridge void *)block;
// .......
}
1. 要想看 block 的底层实现,首先将 OC 代码转换成 C++ 代码
新建 block.m
文件添加以下 OC 代码:
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
__block int b = 20;
NSString *str = @"str";
__weak NSString *weakStr = @"weakStr";
void(^block)(int) = ^(int i){
i;
a;
b;
str;
weakStr;
};
block(1);
}
return 0;
}
上述代码定义了各种类型的变量用于测试
终端 cd 到 block.m
根目录执行一下命令进行转换
clang -rewrite-objc block.m
这个时候会报 error
cannot create __weak reference because the current deployment target does not support weak references
在 stack overflow
上找到这个问题解决方案,通过以下命令进行转换
clang -rewrite-objc -fobjc-arc -stdlib=libc++ -mmacosx-version-min=10.7 -fobjc-runtime=macosx-10.7 -Wno-deprecated-declarations block.m
这时候可以发现在当前目录会生成对应的 C++ 文件 block.cpp
,通过这个文件可以看到 block 在 C++ 中的实现,更好的理解 block 机制
找到其中对应 main 函数的代码
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
int a = 10;
__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 20};
NSString *str = (NSString *)&__NSConstantStringImpl__var_folders_0__w7c3mlv94_10qs2mxt1kk0980000gn_T_block_3ee913_mi_0;
__attribute__((objc_ownership(none))) NSString *weakStr = (NSString *)&__NSConstantStringImpl__var_folders_0__w7c3mlv94_10qs2mxt1kk0980000gn_T_block_3ee913_mi_1;
void(*block)(int) = ((void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, str, weakStr, (__Block_byref_b_0 *)&b, 570425344));
((void (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 1);
}
return 0;
}
2. 解析 C++ 文件
2.1 __Block_byref_b_0
OC 代码
__block int b = 20;
对应 C++ 代码
__attribute__((__blocks__(byref))) __Block_byref_b_0 b = {(void*)0,(__Block_byref_b_0 *)&b, 0, sizeof(__Block_byref_b_0), 20};
实际上 b
就是以下类型
struct __Block_byref_b_0 {
void *__isa;
__Block_byref_b_0 *__forwarding;
int __flags;
int __size;
int b;
};
可以看出被 __block
修饰的 b
是一个与 OC 内存结构兼容的 struct
-
__isa
: 默认为(void*)0
__forwarding
: 在构造函数中传入的是(__Block_byref_b_0 *)&b
,是一个指向b
的指针,在下面 block 的 C++ 实现(2.2 __main_block_impl_0
)中可以看到构造函数传入的就是该指针,因此用__block
修饰的变量实际传入 block 的是一个指向该变量的指针,因此可以对其进行修改__size
: 大小b
:b 的值 20
2.2 __main_block_impl_0
OC 代码
void(^block)(int) = ^(int i){
i;
a;
b;
str;
weakStr;
};
对应 C++ 代码
void(*block)(int) = ((void (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, str, weakStr, (__Block_byref_b_0 *)&b, 570425344));
实际上 block
就是以下类型
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
NSString *__strong str;
NSString *__weak weakStr;
__Block_byref_b_0 *b; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, NSString *__strong _str, NSString *__weak _weakStr, __Block_byref_b_0 *_b, int flags=0) : a(_a), str(_str), weakStr(_weakStr), b(_b->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看到 block
实际也是一个结构体,内部包含一个 __block_impl
的结构体、__main_block_desc_0
结构体指针,以及用到的外部变量 a
,b
,str
,weakStr
和一个构造函数
通过观察构造函数,可以看出外部变量传递到 block 中时,只有用 __block
修饰过的 b
传入的是 __Block_byref_b_0 *
指针,对 b
的修改操作都是通过 _b->__forwarding
来直接对原始b
数据进行修改。而其他未用 __block
只是进行赋值操作,对 a
,str
, weakStr
的修改无法影响到 block 结构体内部的对应的属性的值。
因此会产生以下代码的差异
#import <Foundation/Foundation.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
int a = 10;
__block int b = 10;
void(^block)(void) = ^ {
NSLog(@"%d,%d", a, b);
};
a = 20;
b = 20;
block(); // 输出 10,20
}
return 0;
}
2.3 __block_impl
__block_impl
结构体的定义如下,保存 block 的实现
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
-
isa
: 表示一个类对象指针,指向block
的类。我们可以看到在__main_block_impl_0
构造函数中有以下代码impl.isa = &_NSConcreteStackBlock;
isa
被赋值为&_NSConcreteStackBlock
,指向类__NSStackBlock__
的指针,说明了该 block 的是一个存放在栈区的 block,下面3. Block 类型
有对 block 的类型进行介绍 Flags
:标记位,标记 desc(2.4 __main_block_desc_0
) 中有无 copy , dispose方法,有无方法签名字符 signature,layout 等FuncPtr
:指针指向 block 的具体实现,看main
方法中传入的参数为方法__main_block_func_0
2.4 __main_block_desc_0
__main_block_desc_0
结构体的定义如下,保存 block 描述信息
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
// 以下内容在本例中没有,但是若 __block_impl 中 Flags 有标记位 1 << 30 则会有
// const char *signature;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
reserved
: 0Block_size
:block 大小copy
:copy 传入的是方法__main_block_copy_0
,实现的功能就是当 block 进行copy
时,会调用该方法。具体实现如下
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->b, (void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_assign((void*)&dst->str, (void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_assign((void*)&dst->weakStr, (void*)src->weakStr, 3/*BLOCK_FIELD_IS_OBJECT*/);}
dispose
:dispose 传入的是方法__main_block_dispose_0
,实现的功能就是当 block 进行从堆中移除时调用。具体实现如下
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->b, 8/*BLOCK_FIELD_IS_BYREF*/);_Block_object_dispose((void*)src->str, 3/*BLOCK_FIELD_IS_OBJECT*/);_Block_object_dispose((void*)src->weakStr, 3/*BLOCK_FIELD_IS_OBJECT*/);}
signature
:在本例中没有,但是若 block_impl(2.3 __block_impl
) 中 Flags 有标记位 1 « 30 则会有,对方法参数的签名,对应NSMethodSignature
2.5 __main_block_func_0
OC 代码
{
i;
a;
b;
str;
weakStr;
};
对应 C++ 代码,方法 __main_block_func_0 定义如下
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int i) {
__Block_byref_b_0 *b = __cself->b; // bound by ref
int a = __cself->a; // bound by copy
NSString *__strong str = __cself->str; // bound by copy
NSString *__weak weakStr = __cself->weakStr; // bound by copy
i;
a;
(b->__forwarding->b);
str;
weakStr;
}
__cself
指向 block 对象,只有用 __block
修饰过的变量会用特殊的方式调用 b->__forwarding->b
3. Block 类型
这里对 Block 类型的理解有点问题,后来重新测试 Block 的时候发现了我理解是有偏颇的,因此删除了这部分内容 一些我对 block 类型的理解重新写在了 iOS - Block 原理探究(2)