1、如下代码,会出现什么问题?
@interface Person : NSObject
@property(nonatomic, copy) NSString *name;
@property(nonatomic, copy) void(^block)(void);
@end
@implementation Person
- (void)test {
self.block = ^{
NSLog(@"%@",_name);
};
}
@end
解答:会出现循环引用问题,并且xcode会触发2条编译警告⚠️
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior
Capturing 'self' strongly in this block is likely to lead to a retain cycle
对成员变量在block中直接访问,造成block->Person
的强引用,Person->block
的强引用,最终造成循环引用。
修改代码:
- (void)test {
self.block = ^{
NSLog(@"%@",self->_name);
};
}
这种情况下,会造成循环引用,Xcode编译器会发出⚠️
Capturing 'self' strongly in this block is likely to lead to a retain cycle
继续修改代码
- (void)test {
Person *p = self;
self.block = ^{
NSLog(@"%@",p->_name);
//NSLog(@"%@",p.name);
};
}
这种情况下,会造成循环引用问题,编译器不会警告
继续修改代码
- (void)test {
__block Person *p = self;
self.block = ^{
NSLog(@"%@",p.name);
};
}
这种情况下,依然会造成循环引用问题
继续修改代码
- (void)test {
Person *p = [Person new];
p.block = ^{
NSLog(@"%@",_name);
};
}
这种情况下,不会造成循环引用问题,但Xcode会发出编译器警告⚠️
Block implicitly retains 'self'; explicitly mention 'self' to indicate this is intended behavior
继续修改代码
- (void)test {
__weak Person *p = self;
self.block = ^{
NSLog(@"%@",p.name);
};
}
这种情况下,不会造成循环引用。dealloc正常被调用
继续修改代码
- (void)test {
__unsafe_unretained Person *p = self;
self.block = ^{
NSLog(@"%@",p.name);
};
}
这种情况下,不会造成循环引用。
问题1总结:
- block里面使用self并不一定都会造成循环引用
- self强引用了block,则block中使用self或者成员变量都会造成循环引用
- block不能解决循环引用,weak和__unsafe_unretained能解决循环引用问题
2、weak和strong,为什么需要使用__strong
__weak __typeof(self)weakSelf = self; //1
[self.context performBlock:^{
[weakSelf doSomething]; //2
__strong __typeof(weakSelf)strongSelf = weakSelf; //3
[strongSelf doSomething];
}]
- 使用weak typeof是在编译的时候,另外创建一个weak对象来操作self。
- 因为weakSelf和self是两个内容,doSomething有可能就直接对self自身引用计数减到0了. 所以在[weakSelf doSomething]的时候,你很难控制这里self是否就会被释放了.weakSelf只能看着。
- strong typeof在编译的时候,实际是对weakSelf的强引用。指针连带关系self的引用计数还会增加.但是你这个是在block里面,生命周期也只在当前block的作用域。所以,当这个block结束, strongSelf随之也就被释放了.同时也不会影响block外部的self的生命周期。
3、Block对变量的捕获
如下代码,打印的结果是多少?
- (void)test2 {
NSInteger a = 3; // 局部变量
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n * a;
};
a = 1;
printf("%zd \n",block(2));
}
打印结果:
打印的结果是6,Block对局部变量的截获并没有因为a重新赋值而发生变化。
继续修改代码
- (void)test2 {
static NSInteger a = 3; //局部静态变量
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n * a;
};
a = 1;
printf("%zd \n",block(2));
}
打印结果
打印的结果是2,a发生变化之后block内部的a也发生了改变。
继续修改代码
NSInteger a = 3; //全局变量
- (void)test2 {
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n * a;
};
a = 1;
printf("%zd \n",block(2));
}
打印结果
从打印结果发现,全局变量a修改之后影响到了Block内部的a发生变化
继续修改代码
static NSInteger a = 3;
- (void)test2 {
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n * a;
};
a = 1;
printf("%zd \n",block(2));
}
打印结果
打印输出是2,全局静态变量a在修改之后也直接影响到了block中的值
继续修改代码
- (void)test2 {
__block NSInteger a = 3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n * a;
};
a = 1;
printf("%zd \n",block(2));
}
打印结果
打印输出是2,说明__block修饰的局部变量在Block内部是指针捕获
继续修改代码
__block NSInteger a = 3;
- (void)test2 {
NSInteger(^block)(NSInteger) = ^NSInteger(NSInteger n){
return n * a;
};
a = 1;
printf("%zd \n",block(2));
}
编译报错,提示❌
__block attribute not allowed, only allowed on local variables
**
使用clang查看局部变量,全局变量,局部静态变量,全局静态变量,__block变量的底层代码结构
源代码:
NSInteger n1 = 3;
static NSInteger n2 = 3;
- (void)test3 {
NSInteger n3 = 3;
static NSInteger n4 = 3;
__block NSInteger n5 = 3;
void(^block)(void) = ^void(){
NSLog(@"%zd",n1);
NSLog(@"%zd",n2);
NSLog(@"%zd",n3);
NSLog(@"%zd",n4);
NSLog(@"%zd",n5);
};
block();
}
clang编译后的C++代码
NSInteger n1 = 3;
static NSInteger n2 = 3;
struct __Block_byref_n5_1 {
void *__isa;
__Block_byref_n5_1 *__forwarding;
int __flags;
int __size;
NSInteger n5;
};
struct __Person__test3_block_impl_0 {
struct __block_impl impl;
struct __Person__test3_block_desc_0* Desc;
NSInteger n3;
NSInteger *n4;
__Block_byref_n5_1 *n5; // by ref
__Person__test3_block_impl_0(void *fp, struct __Person__test3_block_desc_0 *desc, NSInteger _n3, NSInteger *_n4, __Block_byref_n5_1 *_n5, int flags=0) : n3(_n3), n4(_n4), n5(_n5->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __Person__test3_block_func_0(struct __Person__test3_block_impl_0 *__cself) {
__Block_byref_n5_1 *n5 = __cself->n5; // bound by ref
NSInteger n3 = __cself->n3; // bound by copy
NSInteger *n4 = __cself->n4; // bound by copy
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_1,n1);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_2,n2);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_3,n3);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_4,(*n4));
NSLog((NSString *)&__NSConstantStringImpl__var_folders_5m_900ng1910q18rfy4jrhcbdm40000gn_T_Person_ef54cb_mi_5,(n5->__forwarding->n5));
}
static void __Person__test3_block_copy_0(struct __Person__test3_block_impl_0*dst, struct __Person__test3_block_impl_0*src) {_Block_object_assign((void*)&dst->n5, (void*)src->n5, 8/*BLOCK_FIELD_IS_BYREF*/);}
static void __Person__test3_block_dispose_0(struct __Person__test3_block_impl_0*src) {_Block_object_dispose((void*)src->n5, 8/*BLOCK_FIELD_IS_BYREF*/);}
static struct __Person__test3_block_desc_0 {
size_t reserved;
size_t Block_size;
void (*copy)(struct __Person__test3_block_impl_0*, struct __Person__test3_block_impl_0*);
void (*dispose)(struct __Person__test3_block_impl_0*);
} __Person__test3_block_desc_0_DATA = { 0, sizeof(struct __Person__test3_block_impl_0), __Person__test3_block_copy_0, __Person__test3_block_dispose_0};
static void _I_Person_test3(Person * self, SEL _cmd) {
NSInteger n3 = 3;
static NSInteger n4 = 3;
__attribute__((__blocks__(byref))) __Block_byref_n5_1 n5 = {(void*)0,(__Block_byref_n5_1 *)&n5, 0, sizeof(__Block_byref_n5_1), 3};
void(*block)(void) = ((void (*)())&__Person__test3_block_impl_0((void *)__Person__test3_block_func_0, &__Person__test3_block_desc_0_DATA, n3, &n4, (__Block_byref_n5_1 *)&n5, 570425344));
((void (*)(__block_impl *))((__block_impl *)block)->FuncPtr)((__block_impl *)block);
}
分析以上C++代码,可以知道,Block对全局变量,静态全局变量是直接使用,在Block内部没有做任何操作,所以Block没有对全局变量和静态全局变量进行捕获。
从__Person__test3_block_impl_0
内部可以看出block转化为结构体对象之后,会自动生成对应的属性 NSInteger n3;``NSInteger *n4;``__Block_byref_n5_1 *n5;
,block修饰的局部变量也会生成一个结构体Block_byref_n5_1。可以看出NSInteger *n4;__Block_byref_n5_1 *n5;
是指针类型。
再继续分析Persontest3_block_func_0可以看出,这里进行了一次赋值操作
__Block_byref_n5_1 *n5 = __cself->n5; // bound by ref
NSInteger n3 = __cself->n3; // bound by copy
NSInteger *n4 = __cself->n4; // bound by copy
可以看出是对生成的结构体属性进行赋值,(n5->__forwarding->n5)
这里可以看出block修饰的变量生成的结构体对象中`forwarding`指针指向了变量本身
总结: