Block对外部变量的引用
1.所有人都只知道在不考虑 __block
情况下,block对外部变量的会进行copy
,在iOS copy分为mutableCopy
和copy
,那么block copy是哪一种呢?一个很常见的例子:
int a= 1;
int b = 2;
int (^sum)(void) = ^(void) {
return a * b;
};
NSLog(@"%d", sum()); // 3
a = 5;
b = 6;
NSLog(@"%d", sum()); //3
我想大家对这对代码的结果很清楚,根据两次相同的结果我们知道sum
对a
,b
进行了copy
,而这种copy
应该是mutableCopy
,重新开辟了一段内存空间来存储a
,b
。
接下来一个非常相似的代码:
Person *p = [[Person alloc]init];
p.name = @"yan";
void (^test)(void) = ^() {
p.name = @"zhao";
NSLog(@"%@", p);
};
test();
NSLog(@"%@", p.name);
我想大家一定不会觉得输出来的还是yan
, 凭直觉应该是zhao
。答案就是zhao
。大家可以自己做一个小程序试试,对比这个两个我们应该会发现一些问题,为什么对于基本类型(int float double
)输出的结果不影响,但是对于OC 对象就影响呢。答案就是a
和b
进行的是深复制,而对象p
进行的浅复制。
总结:block对基本类型进行的是深复制,但是对OC对象(没有被__block
修饰)进行的是浅复制,只是增加OC对象的retainCount。
2.继续第一步,我们是在没有__block
修饰的情况下进行的。如果我没有__block
进行修饰,那么在block中没有权利进行修改(这里的修改是指指针的变动,而不是某个对象属性的改变),当我们加上__block修饰符的时候,block不会对对象进行复制。也就是说block不会拥有引用的外部OC对象,代码里子:
Person *p = [[Person alloc]init]; // retainCount = 1
p.name = @"yan";
void (^test)(void) = ^() {
p.name = @"zhao"; // retainCount = 3
NSLog(@"%@", p);
};
test();
NSLog(@"%@", p.name); // retainCount = 3
__block Person *p = [[Person alloc]init]; // retainCount = 1
p.name = @"yan";
void (^test)(void) = ^() {
p.name = @"zhao"; // retainCount = 1
NSLog(@"%@", p);
};
test();
NSLog(@"%@", p.name); // retainCount = 1
根据代码可以发现,p的retainCount没有增加--- 题外话:怎么在ARC下查看retainCount: CFGetRetainCount((__bridge CFTypeRef)(p))
这时候我们可以引入出来另外一个话题就是block的循环引用
3.block循环引用
block会对外部的变量进行复制(也就是强引用),当我们在block中使用 实例变量, 发送消息。block都会对self进行强引用。这样很容易形成retain cycle。那么应该怎么样进行解决呢。我在这里总结一下:
__weak __typeof(self) weakSelf = self;
void (^test)(void) = ^() {
if (!weakSelf) return;
weakSelf.name = @"zhao";
};
__weak __typeof(self) weakSelf = self;
void (^test)(void) = ^() {
__Strong __typeof(weakSelf) strongSelf = weakSelf;
strongSelf.name = @"zhao";
};
前面这两种是非常常见得。而且也是比较正规的。if (!weakSelf) return;
__Strong __typeof(weakSelf) strongSelf = weakSelf;
都是为了防止self
为nil
__block Viewcontoller *vc = self;
void (^test)(void) = ^() {
vc.name = @"zhao";
};
4.block的分类
- NSGlobalBlock: 没有引入外部变量
- NSStackBlock:在
MRC
下,引用了外部变量是放在栈上的。在ARC
应该用__weak
修饰 - NSMallocBlock:在
MRC
下需要进行Block_copy
,在ARC
只要有强指针指向就可以放在堆上。
如果block 是 Stack:
Person *p = [[Person alloc]init]; // retainCount = 1
p.name = @"yan";
void (^test)(void) = ^() {
p.name = @"zhao"; // retainCount = 2
NSLog(@"%@", p);
};
test();
NSLog(@"%@", p.name); // retainCount = 2
与原来相比对P的retainCount的操作只增加1,而不是2.
5.问题
-
strong block
为什么对对象的进行复制时,对象的retaincount
直接增加2.而weak block
只增加 1? 希望大家可以指教一二。