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