前言
文章是阅读《Objective-C高级编程:iOS与OS X多线程和内存管理》之后的一些理解总结。
理解Block如何实现,需要了解
什么是Block
Block
是C
语言的拓展功能。在Apple文档中有一句话说明:
1
| In other languages and environments, a block object is sometimes also called a closure or a lambda.
|
其他语言和环境,一个block
对象有时候也被称作closure
闭包或^
;
在阮一峰的学习Javascript闭包(Closure)中解释了什么是闭包。
闭包就是能够读取其他函数内部变量的函数。
Block的语法
声明一个Block
1
| NSUInteger (^lengthBlock)(NSString *);
|
NSUInteger
是Block
返回的值类型
^lengthBlock
表示声明了名字lengthBlock
的Block
NSString *
表示NSString
类型的参数
创建一个Block
1 2 3 4
| lengthBlock = ^(NSString *str) { return str.length; }; NSLog(@"%lu",(unsigned long)lengthBlock(@"123"));
|
^(NSString *str)
对lengthBlock
的定义,分配给lengthBlock
的值
str
参数名称
{...}
大括弧是Block
的主体
return str.length
是lengthBlock
的返回值
Block
可以直接使用相同的作用域内定义的变量
1 2 3 4 5 6
| NSString *string = @"123"; NSUInteger (^lengthBlock)(NSString *) = ^(NSString *str){ return string.length + str.length; }; NSLog(@"%lu",(unsigned long)lengthBlock(@"456"));
|
Block类型变量
1 2 3 4 5 6 7 8
| - (void)stringWithLengthBlock:(NSUInteger (^)(NSString *))lengthBlock{ NSLog(@"%lu",lengthBlock(@"456")); } [self stringWithLengthBlock:^NSUInteger(NSString *str) { NSString *string = @"123"; return string.length + str.length; }];
|
也可以使用typedef
声明
1 2 3 4 5
| typedef void (^Block)(NSString *str); - (void)stringWithLengthBlock:(Block)block{ block(@"123"); }
|
Block的实现
举个🌰
1 2 3 4 5
| int main(int argc, const char * argv[]) { void (^block)(void) = ^{printf("Block\n");}; block(); return 0; }
|
使用clang使用编译器改写文件
这里需要注意的是编译后的代码只是为了理解Block的实现,并不是Block的源码
下面转换例子代码后Block
的相关代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| 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[]) { void (*block)(void) = ((void (*) ())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); return 0; }
|
我们可以看到第一个__main_block_impl_0
结构体包含__block_impl
和__main_block_desc_0
两个结构体
1 2 3 4 5 6
| struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
|
我们来看一下Block结构体的构成
isa
指针,用于指向Block的指针
Flags
表示Block附加的信息
Reserved
保留变量
FuncPtr
函数指针指向Block实现的函数地址
其中isa
指向Block有三种类型:
1 2 3
| isa = &_NSConcreteStackBlock; isa = &_NSConcreteGlobalBlock; isa = &_NSConcreteMallocBlock;
|
__main_block_desc_0
的构成,用来描述Block一些信息
1 2 3 4
| 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)};
|
reserved
保留变量
Block_size
Block的大小
下面看一下初始化
1
| impl.isa = &_NSConcreteStackBlock;
|
_NSConcreteStackBlock
用来初始化了__block_impl
结构体的isa
这里为什么使用_NSConcreteStackBlock
来初始化,说明Block
创建在Stack
上
接下来看 int main{}
中的调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); &__main_block_impl_0 ((void *) __main_block_func_0, &__main_block_desc_0_DATA) __main_block_impl_0 impl = (__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA)); void (*block)(void) =((void (*)())&__main_block_impl_0() struct __main_block_impl_0 *block = &impl;
|
将__main_block_impl_0
结构的impl
实例地址赋值给*block
函数指针
1 2 3 4
| ((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block); (*block ->impl.FuncPtr)(block);
|
代码使用了函数指针调用函数
取出block
的函数指针调用FuncPtr
,以自身(block
)为参数传入
总结一下Block
简单的实现流程
1 2 3 4 5 6 7 8 9
| main(); ⬇︎ **初始化__main_block_impl_0 结构体** ⬇︎ **将结构体地址赋值给*block函数指针** ⬇︎ **调用block函数指针的FuncPtr函数** ⬇︎ **结束**
|
结尾
Block
还有截获自动变量、__block修饰符、block存储域即上文提到的三种block类型,内存管理等等问题待我细细研习之后再写总结。
参考资料
<!— more –>