Block 01 - 底层实现
Block 是一个封装了函数调用以及函数调用环境的 Objective-C 对象。
使用 Block 可以实现闭包,它是对 C 语言的一种扩充功能,我们的代码块在底层被封装成了一个函数,函数指针是 Block 底层结构体的一个成员,Block 底层有对外部局部变量捕获的机制。
Block 的底层实现
// Block 底层实现的基础结构体
struct __block_impl {
void *isa; // Block Class 对象的指针
int Flags; // 标志变量,在实现 Block 的内部操作时会用到
int Reserved; // 保留字段
var *FuncPrt; // 功能函数的指针
};
// Block 的底层实现,Block 本质是一个 Objective-C 对象
//
// 当 Block 在栈上时,不会对 auto 变量指向的对象、__block 对象进行强引用。
struct __xxx_block_impl_0 {
struct __block_impl impl; // 底层实现的基础结构体
struct __xxx_block_desc_0 *Desc; // Block 的描述信息
// auto_ivars... // 被捕获的局部 auto 变量
// *block_byref_ivars... // 被捕获的局部 auto 变量(__block 对象)
// *static_ivars... // 被捕获的局部 static 变量
// 全局变量不需要捕获,在功能函数中直接访问
// Constructor
//
// *fp: 功能函数的指针
// *desc:当前结构体占用内存的大小
// _auto_vars: 外部的局部 auto 变量(值传递)
// *_block_byref_ivars: 外部的局部 auto 变量(__block 对象,引用传递)
// *_static_vars: 外部的局部 static 变量(引用传递)
__xxx_block_impl_0(void *fp, struct __xxx_block_desc_0 *desc,
_auto_vars..., *_block_byref_ivars..., *_static_vars...,
int flags = 0) :
auto_ivar0(_auto_var0)...,
block_byref_ivar0(_block_byref_ivar0)...,
static_ivar0(_static_var0)... {
// _NSConcreteGlobalBlock _NSConcreteStackBlock _NSConcreteMallocBlock
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPrt = fp;
Desc = desc;
}
};
// Block 的描述信息
static struct __xxx_block_desc_0 {
size_t reserved; // 保留字段
size_t Block_size; // Block 占用内存的大小
void (*copy)(struct __xxx_block_impl_0*, struct __xxx_block_impl_0*); // 为 auto 变量指向的对象、__block 对象做内存管理
void (*dispose)(struct __xxx_block_impl_0*); // 为 auto 变量指向的对象、__block 对象做内存管理
} __xxx_block_desc_0_DATA = { 0, sizeof(struct __xxx_block_impl_0),
__xxx_block_copy_0, __xxx_block_dispose_0 };
// 当 Block 被拷贝到堆上时:
// 会调用 Block 内部的 copy 函数。
// copy 函数内部会调用 _Block_object_assign 函数。
// _Block_object_assign 函数会对 __block 对象进行强引用。
// _Block_object_assign 函数会根据 auto 变量的修饰符(__strong, __weak, __unsafe_unretained)做出相应的操作,进行强引用(retain)或弱引用。
static void __xxx_block_copy0(struct __xxx_block_impl_0 *dst,
struct __xxx_block_impl_0 *src) {
_Block_object_assign((void*)&dst->auto_ivar0, (void*)src->auto_ivar0, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_assign((void*)&dst->block_byref_ivar0, (void*)src->block_byref_ivar0, 8/*BLOCK_FIELD_IS_BYREF*/);
}
// 当 Block 从堆上移除时:
// 会调用 Block 内部的 dispose 函数。
// dispose 函数内部会调用 _Block_object_dispose 函数。
// _Block_object_dispose 函数会自动释放被强引用的对象(release)。
static void __xxx_block_dispose(struct __xxx_block_impl_0 *src) {
_Block_object_dispose((void*)src->auto_ivar0, 3/*BLOCK_FIELD_IS_OBJECT*/);
_Block_object_dispose((void*)src->block_byref_ivar0, 8/*BLOCK_FIELD_IS_BYREF*/);
}
// 功能函数的底层实现
static void __xxx_block_func_0(struct __xxx_block_impl_0 *__cself, vars...) {
// auto_ivar0 = __cself->auto_ivar0; // bound by copy
// *block_byref_ivar0 = __cself->block_byref_ivar0; // bound by ref
// *static_ivar0 = __cself->static_ivar0; // bound by copy
// ...
// do something...
}
Block 的底层原理
Block 是一个封装了函数调用以及函数调用环境的 Objective-C 对象。
- Block 底层通过结构体实现,内部也有一个 isa 指针。
- Block 内部通过一个指针调用功能函数。
- Block 会对外部局部变量进行捕获。
Block 底层结构图
Block 的变量捕获(Capture)
为了保证 Block 内部能够正常访问外部的局部变量,Block 有变量捕获机制。
auto 变量的捕获示意图:
Block 的类型
Block 有 3 种类型,可以通过 class 方法或 isa 指针查看具体类型,3 种类型都是 NSBlock 的子类。
-
NSGlobalBlock (_NSConcreteGlobalBlock)
- NSGlobalBlock --> __NSGlobalBlock --> NSBlock --> NSObject
-
NSStackBlock (_NSConcreteStackBlock)
- NSStackBlock --> __NSStackBlock --> NSBlock --> NSObject
-
NSMallocBlock (_NSConcreteMallocBlock)
- NSMallocBlock --> __NSMallocBlock --> NSBlock --> NSObject
括号前面的是运行时的 Block 类型,括号内是将 Objective-C 转换为 C++ 后的类型。
Blocks 都派生自 NSBlock,NSBlock 的父类是 NSObject。
Block 所在的内存区域
Block 调用 copy 后的结果
Block 的使用示例
定义完 Block 直接调用:
int sum = ^(int a, int b) {
return a + b;
}(1, 2);
先定义 Block 再调用:
int (^add)(int, int) = ^(int a, int b) {
return a + b;
};
int sum = add(1, 2);
函数接收一个 Block,函数内部调用 Block:
void fetchData(void (^completion)(id result)) {
dispatch_async(dispatch_get_global_queue(0, 0), ^ {
// do something..
completion(@"hello~");
});
}
fetchData(^(id result) {
NSLog(@"result: %@", result);
});
与上面示例的不同之处在于为 Block 先定义一个类型:
typedef void (^Completion)(id result);
void fetchData(Completion completion) {
dispatch_async(dispatch_get_global_queue(0, 0), ^ {
// do something...
completion(@"hello~");
});
}
fetchData(^(id result) {
NSLog(@"result: %@", result);
});
将 Block 作为属性使用:
@interface Player : NSObject
@property (nonatomic, copy) void (^onStop)(void);
- (void)stop;
@end
@implementation Player
- (void)stop {
self.onStop();
}
@end
Player *player = [[Player alloc] init];
player.onStop = ^ {
NSLog(@"停止播放");
};
[player stop];
网友评论