在ARC环境下,编译器会根据情况自动将栈上的block进行一次copy操作,将block复制到堆上。
什么情况下ARC会自动将block进行一次copy操作?以下代码都在ARC环境下执行。
block作为函数返回值时
typedef void (^Block)(void);
Block myblock()
{
int a = 10;
Block block = ^{
NSLog(@"---------%d", a);
};
return block;
}
int main(int argc, const char * argv[]) {
@autoreleasepool {
Block block = myblock();
block();
NSLog(@"%@",[block class]);
// 打印block类型为 __NSMallocBlock__
}
return 0;
}
如果在block中访问了auto变量时,block的类型为__NSStackBlock__,上面打印内容发现blcok为__NSMallocBlock__类型的,并且可以正常打印出a的值,说明block内存并没有被销毁。 block进行copy操作会转化为__NSMallocBlock__类型,说明block是复制到堆上了,那么说明在ARC环境下, block作为函数返回值时编译器会自动帮助我们对block进行copy操作,以保存block,并在适当的地方进行release操作。
将block赋值给__strong指针时
int main(int argc, const char * argv[]) {
@autoreleasepool {
// block内没有访问auto变量 __NSGlobalBlock__ 类型
Block block1 = ^{
NSLog(@"block---------");
};
NSLog(@"1. %@",[block1 class]);
int a = 10;
// block内访问了auto变量,但没有赋值给__strong指针 __NSStackBlock__ 类型
NSLog(@"2.%@",[^{
NSLog(@"block2---------%d", a);
} class]);
// block赋值给__strong指针 __NSMallocBlock__ 类型
Block block3 = ^{
NSLog(@"block2---------%d", a);
};
NSLog(@"3.%@",[block3 class]);
}
return 0;
}
查看打印内容可以看出,当block被赋值给__strong指针时,ARC会自动进行一次copy操作。
block作为Cocoa API中方法名含有usingBlock的方法参数时
遍历数组的block方法,将block作为参数的时候
NSArray *array = @[@"1",@"2"];
[array enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
}];
block作为GCD API的方法参数时
GCD的一次性函数或延迟执行的函数,执行完block操作之后系统才会对block进行release操作。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
});
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
通过上面对MRC及ARC环境下block的不同类型的分析,总结出不同环境下block属性建议写法。
MRC下block属性的建议写法
@property (copy, nonatomic) void (^block)(void);
ARC下block属性的建议写法
@property (strong, nonatomic) void (^block)(void);
@property (copy, nonatomic) void (^block)(void);
何时使用copy和strong
这个是基础,如果不清楚,可以自己去验证