block
的本质:OC中的block
其实是一个封装了函数调用及调用环境的OC对象
block 底层实现数据结构
int age = 20;
void (^block)(int, int) = ^(int a, int b){
NSLog(@"block age -> %d", age);
NSLog(@"block a -> %d", a);
NSLog(@"block b -> %d", b);
};
定义一个block
如上面的代码
使用如下指令在terminal
中执行,把OC代码编译出cpp代码,窥探block cpp 实现的的数据结构
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m
下面截取编译后和上面定义的block
相关代码
// blcok的通用结构
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 描述block的结构体
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
// 当前定义的`block`结构
struct __main_block_impl_0 {
// block 通用结构
struct __block_impl impl;
// block 通用描述
struct __main_block_desc_0* Desc;
// block 捕获的变量
int age;
};
// block 里边封装的函数
static void __main_block_func_0(struct __main_block_impl_0 *__cself, int a, int b) {
int age = __cself->age; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_f1a625_mi_0, age);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_f1a625_mi_1, a);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_f1a625_mi_2, b);
}
// 定义block的描述
static struct __main_block_desc_0 __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
// 声明block
void (*block)(int, int) = ((void (*)(int, int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, age));
// 简化之后的版本,去掉类型转换
void (*block)(int, int) = &__main_block_impl_0(__main_block_func_0,
&__main_block_desc_0_DATA,
age);
把上面定义的block
转化成结构体
__main_block_impl_0 *cBlock = (__bridge __main_block_impl_0 *)block;
打断点,可以看到block
转换成结构体之后的里边存储的数据
里边的函数指针 0x100000e80
指向 block
被调用时的函数0x100000e80
相关demo代码
Block 变量捕获
普通类型的捕获
分别定义不同类型的变量,在block中调用
// 全局变量
int c = 30;
int main(int argc, const char * argv[]) {
@autoreleasepool {
// 局部变量,默认是 auto,这个的autol可以去掉
auto int a = 10;
// 静态变量
static int b = 20;
void (^block)(void) = ^{
NSLog(@"block--> %d, %d, %d", a, b, c);
};
a *= 2;
b *= 2;
c *= 2;
block();
// block--> 10, 40, 60
}
return 0;
}
可以看出,只有局部变量 a 保留了传进去时的值,static和全局变量都会随着外部更改而更改。 接下来把上面的代码编译成cpp代码,编译后的关键代码如下
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
int c = 30;
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a; //接收a的值
int *b; // 接收b的地址
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _a, int *_b, int flags=0) : a(_a), b(_b) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
int *b = __cself->b; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_a896b8_mi_0, a, (*b), c);
}
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[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
auto int a = 10;
static int b = 20;
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, a, &b));
a *= 2;
b *= 2;
c *= 2;
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
上面代码中
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a; //接收a的值
int *b; // 接收b的地址
}
block
的结构体存有 a
的值,b
的地址,使用的时候直接从block
的结构体中取出, 而 c
时全局变量,没有存储。
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
int a = __cself->a; // bound by copy
int *b = __cself->b; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_a896b8_mi_0, a, (*b), c);
}
对象的捕获
@interface AYPerson : NSObject
@property(nonatomic, strong) NSString *name;
@end
@implementation AYPerson
- (void)test{
void (^block)(void) = ^{
NSLog(@"block -> %@", self);
};
block();
}
@end
编译成cpp代码之后的block
结构体如下
struct __AYPerson__test_block_impl_0 {
struct __block_impl impl;
struct __AYPerson__test_block_desc_0* Desc;
AYPerson *self;
};
同样的,这个block
会有指向AYPerson
实例的指针,
如果在block
中调用对象的方法, 也会存有指向对象的指针
- (void)test{
void (^block)(void) = ^{
NSLog(@"block -> %@", [self name]);
};
block();
}
|
|
|
|
V
struct __AYPerson__test_block_impl_0 {
struct __block_impl impl;
struct __AYPerson__test_block_desc_0* Desc;
AYPerson *self;
};
Block
捕获的是当前的对象
如下代码可以证明
AYPerson *p = [[AYPerson alloc] init];
void (^block)(void) = ^{
NSLog(@"main block -> %p", p);
};
block();
NSLog(@"out side block->%p", p);
p = [[AYPerson alloc] init];
block();
NSLog(@"out side block->%p", p);
/**
MyBlockCapture[44473:892748] main block -> 0x1006ade10
MyBlockCapture[44473:892748] out side block->0x1006ade10
MyBlockCapture[44473:892748] main block -> 0x1006ade10
MyBlockCapture[44473:892748] out side block->0x1006b96e0
*/
block的结构体
|
|
|
|
V
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
AYPerson *p;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, AYPerson *_p, int flags=0) : p(_p) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
可以看出block
会存有传入对象的地址,并持有它
Block的类型
block
有3种类型,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock
类型
__NSGlobalBlock__
( _NSConcreteGlobalBlock
)
__NSStackBlock__
( _NSConcreteStackBlock
)
__NSMallocBlock__
( _NSConcreteMallocBlock
)
在MRC模式下
int a = 20;
void (^block1)(void) = ^{
NSLog(@"test1");
};
void (^block2)(void) = ^{
NSLog(@"test1 - %d", a);
};
void (^block3)(void) = [^{
NSLog(@"test1 - %d", a);
} copy];
NSLog(@"block1 -> %@, %@, %@, %@", [block1 class], [[block1 class]superclass], [[[block1 class] superclass] superclass], [[[[block1class] superclass] superclass] superclass]);
NSLog(@"block2 -> %@, %@, %@, %@", [block2 class], [[block3 class]superclass], [[[block2 class] superclass] superclass], [[[[block2class] superclass] superclass] superclass]);
NSLog(@"block3 -> %@, %@, %@, %@", [block3 class], [[block3 class]superclass], [[[block3 class] superclass] superclass], [[[[block3class] superclass] superclass] superclass]);
/**
block1 -> __NSGlobalBlock__, __NSGlobalBlock, NSBlock, NSObject
block2 -> __NSStackBlock__, __NSMallocBlock, NSBlock, NSObject
block3 -> __NSMallocBlock__, __NSMallocBlock, NSBlock, NSObject
*/
如上可以看到不同类型的创建及继承类型,所有blcok都继承 NSBlock
, NSBlock
继承NSObject
。
__NSStackBlock__, __NSMallocBlock__
的比较
在MRC环境中
void (^myBlock1)(void);
void (^myBlock2)(void);
void test()
{
int age = 10;
myBlock1 = ^{
NSLog(@"my block1 %d", age);
};
myBlock2 = [^{
NSLog(@"my block2 %d", age);
} copy];
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
test();
myBlock1();
// my block1 -272632728
// __NSStackBlock__ 的block 超出定义区间就会被释放
myBlock2();
// my block2 10
// __NSMallocBlock__ 的block 存在堆区, 超出定义区间不会被释放
}
return 0;
}
Block 的 copy
在ARC环境下,编译器会根据情况自动将栈上的block
复制到堆上
block
作为函数返回值时- 将
block
赋值给__strong
指针时 block
作为Cocoa API中方法名含有usingBlock的方法参数时[@[] sortedArrayUsingComparator:^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2)cmptr]
block
作为GCD API的方法参数时dispatch_async(dispatch_get_main_queue(), ^(void)block)
typedef void(^MYBlock)(void);
MYBlock test()
{
int age = 10;
return ^{
NSLog(@"Hello %d", age);
};
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
int la = 30;
MYBlock a = test();
__weak MYBlock b = ^{
NSLog(@"^^D%d", la);
};
MYBlock c = ^{
NSLog(@"^^D%d", la);
};
// 函数返回值
NSLog(@"TypeA: %@", [a class]);
// 弱引用 __weak
NSLog(@"TypeB: %@", [b class]);
// 强引用 __strong
NSLog(@"TypeC: %@", [c class]);
// 无引用
NSLog(@"Stack: %@", [^{
NSLog(@"^^D%d", la);
} class]);
/**
TypeA: __NSMallocBlock__
TypeB: __NSStackBlock__
TypeC: __NSMallocBlock__
Stack: __NSStackBlock__
*/
}
return 0;
}
MRC
下block
属性的建议写法
@property (copy, nonatomic) void (^block)(void);
ARC
下block
属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
对象类型的引用机制
- 当
block
内部访问了对象类型的auto
变量时如果
block
是在栈上,将不会对auto
变量产生强引用
__weak void (^myblock)(void);
{
AYPerson *p = [[AYPerson alloc] init];
__weak typeof(p) weakP = p;
myblock = ^{
NSLog(@"inside-%@", p);
};
}
myblock();
NSLog(@"---------------------");
/**
<AYPerson: 0x1007185f0>-dealloc
inside-(null)
---------------------
*/
上面代码中使用
__weak
修饰block
,block
不持有AYPerson
超出作用域就释放了
- 如果block被拷贝到堆上
void (^myblock)(void);
{
AYPerson *p = [[AYPerson alloc] init];
__weak typeof(p) weakP = p;
myblock = ^{
NSLog(@"inside-%@", p);
};
}
myblock();
把上面的代码调用如下指令
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
编译成cpp代码,block
定义的关键代码如下
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
AYPerson *__strong p;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, AYPerson *__strong _p, int flags=0) : p(_p) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
AYPerson *__strong p = __cself->p; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_be3335_mi_1, p);
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->p, (void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->p, 3/*BLOCK_FIELD_IS_OBJECT*/);}
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*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
会调用
block
内部的copy
函数
copy
函数内部会调用_Block_object_assign
函数_Block_object_assign
函数会根据auto
变量的修饰符(__strong、__weak、__unsafe_unretained)
做出相应的操作,形成强引用(retain)
或者弱引用
如果
block
从堆上移除
会调用block
内部的dispose
函数,dispose
函数内部会调用_Block_object_dispose
函数,_Block_object_dispose
函数会自动释放引用的auto
变量(release)
函数 | 调用时机 |
---|---|
copy函数 | 栈上的Block复制到堆时 |
dispose函数 | 堆上的Block被废弃时 |
__block修饰符
在block
内部想修改 auto
类型的变量时需要使用__block
进行修饰
__block int age = 10;
void (^block)(void) = ^{
age = 20;
NSLog(@"inside - %d", age);
};
block();
// inside - 20
使用__block
修饰的变量编译时会被转换成一个对象__Block_byref_xxx_x
把上面的代码使用
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc -fobjc-arc -fobjc-runtime=ios-8.0.0 main.m
编译成cpp代码,截取编译后的关键代码如下
struct __Block_byref_age_0 {
void *__isa; //类型指针
__Block_byref_age_0 *__forwarding; //指向当前结构体的指针
int __flags;
int __size;
int age; // 被包装的变量
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_age_0 *age = __cself->age; // bound by ref
(age->__forwarding->age) = 20;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_jh_6_2dc41d2nn08vfc324ycsj00000gn_T_main_7a89f8_mi_0, (age->__forwarding->age));
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->age, (void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->age, 8/*BLOCK_FIELD_IS_BYREF*/);}
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*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
age
被编译成 __Block_byref_age_0
对象,里边有一个 int age
用来存age
的值,block
中存有__Block_byref_age_0 *age;
, 修改的时候通过(age->__forwarding->age) = 20;
来修改结构体中的age
. 因为__main_block_impl_0
中存有__Block_byref_age_0
对象,所有会有__main_block_copy_0
和__main_block_dispose_0
方法用来管理对象的引用及释放。
__block修饰的对象类型
__block NSObject *obj = [[NSObject alloc] init];
void (^block)(void) = ^{
obj = nil;
};
NSLog(@"ouside- %d, %@", age, obj);
block();
NSLog(@"ouside- %d, %@", age, obj);
/**
ouside- <NSObject: 0x100798d90>
ouside- 20, (null)
*/
把上面的代码编译成cpp代码,截取关键部分
struct __Block_byref_obj_1 {
void *__isa;
__Block_byref_obj_1 *__forwarding;
int __flags;
int __size;
void (*__Block_byref_id_object_copy)(void*, void*);
void (*__Block_byref_id_object_dispose)(void*);
NSObject *__strong obj;
};
static void __Block_byref_id_object_copy_131(void *dst, void *src) {
_Block_object_assign((char*)dst + 40, *(void * *) ((char*)src + 40), 131);
}
static void __Block_byref_id_object_dispose_131(void *src) {
_Block_object_dispose(*(void * *) ((char*)src + 40), 131);
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_obj_1 *obj; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_obj_1 *_obj, int flags=0) : obj(_obj->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_obj_1 *obj = __cself->obj; // bound by ref
(obj->__forwarding->obj) = __null;
}
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->obj, (void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->obj, 8/*BLOCK_FIELD_IS_BYREF*/);}
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*);
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0), __main_block_copy_0, __main_block_dispose_0};
int main(int argc, const char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
__attribute__((__blocks__(byref))) __Block_byref_age_0 age = {(void*)0,(__Block_byref_age_0 *)&age, 0, sizeof(__Block_byref_age_0), 10};
__attribute__((__blocks__(byref))) __Block_byref_obj_1 obj = {(void*)0,(__Block_byref_obj_1 *)&obj, 33554432, sizeof(__Block_byref_obj_1), __Block_byref_id_object_copy_131, __Block_byref_id_object_dispose_131, ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc")), sel_registerName("init"))};
void (*block)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_age_0 *)&age, (__Block_byref_obj_1 *)&obj, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
return 0;
}
跟普通类型相比,被__block
修饰的对象类型多了内存管理相关函数__Block_byref_id_object_copy_131
,__Block_byref_id_object_dispose_131
.
- 当
__block
变量被copy
到堆时会调用
__block
变量内部的copy
函数 copy函数内部会调用_Block_object_assign
函数_Block_object_assign
函数会根据所指向对象的修饰符(__strong、__weak、__unsafe_unretained)
做出相应的操作,形成强引用(retain)
或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain) - 如果
__block
变量从堆上移除会调用
__block
变量内部的dispose
函数dispose
函数内部会调用_Block_object_dispose
函数_Block_object_dispose
函数会自动释放指向的对象(release)
循环引用及解决
typedef void(^MyBlock)(void);
@interface AYPerson : NSObject
@property(nonatomic, copy) MyBlock block;
@property(nonatomic, assign) int age;
@end
@implementation AYPerson
- (void)test
{
__weak typeof(self) weakSelf = self;
self.block = ^{
NSLog(@"%@", self);
};
}
@end
给对象添加一个block
属性,并且让block
捕获对象本身就会造成循环引用,导致内存泄漏。
解决循环引用的问题
- 使用
__weak
,__unsafe_unretained
解决__weak typeof(self) weakSelf = self; self.block = ^{ NSLog(@"%@", weakSelf); };
或
__unsafe_unretained typeof(self) weakSelf = self; self.block = ^{ NSLog(@"%@", weakSelf); };
- 使用
__block
解决__block id weakSelf = self; self.block = ^{ NSLog(@"%@", weakSelf); weakSelf = nil; }; self.block();
但必须调用
block
.