以前只知道block可以捕捉环境中的变量并且会拷贝该变量,但是是深拷贝还是浅拷贝呢?对于这个问题一直很模糊,今天用几个例子来研究一下这个问题。
先看个例子1:
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
NSLog(@"之前 - %p", array);
void (^block)() = ^{
NSLog(@"之后 - %p", array);
};
block();
2015-11-11 13:12:34.252 俩个在不同文件中相同名称函数会冲突[1595:122276] 之前 - 0x7f8e11430fe0
2015-11-11 13:12:34.253 俩个在不同文件中相同名称函数会冲突[1595:122276] 之后 - 0x7f8e11430fe0
可以发现对象的地址没有变,也就是block没有改变对象。
例子2 :
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
NSLog(@"之前 - %p", &array);
void (^block)() = ^{
NSLog(@"之后 - %p", &array);
};
block();
2015-11-11 13:19:05.271 俩个在不同文件中相同名称函数会冲突[1627:126081] 之前 - 0x7fff5045c918
2015-11-11 13:19:05.272 俩个在不同文件中相同名称函数会冲突[1627:126081] 之后 - 0x7fde4bc68400
可以发现引用对象的指针发生改变了。
小结:block会对外部局部变量进行浅拷贝,然后在block体内使用该副本。
例子3:改变array指针指向,再调用块,会输出原数组对象还是新的数组对象呢?
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
void (^block)() = ^{
NSLog(@"%@", array);
};
block();
array = [NSMutableArray arrayWithObjects:@"3", @"4", nil];
block();
2015-11-11 13:22:46.342 俩个在不同文件中相同名称函数会冲突[1647:128664] (
1,
2
)
2015-11-11 13:22:46.343 俩个在不同文件中相同名称函数会冲突[1647:128664] (
1,
2
)
根据上面俩个例子得出的小结很容易解释,block体内和外的array是俩个指针,改变体外的指针自然不会影响到体内的指针了,而体内的指针指向的是原array对象,所以输出是原对象。
例子4 :在例子3中,如果改变对象,那么应该会对输出产生影响吗?
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
void (^block)() = ^{
NSLog(@"%@", array);
};
block();
[array addObject:@"3"];
block();
2015-11-11 13:35:39.823 俩个在不同文件中相同名称函数会冲突[1675:133524] (
1,
2
)
2015-11-11 13:35:39.824 俩个在不同文件中相同名称函数会冲突[1675:133524] (
1,
2,
3
)
可以看到输出改变了。
例子5 :
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableArray *array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
NSLog(@"之前 - %p %@", array, array);
void (^block)() = ^{
NSLog(@"之后 - %p %@", array, array);
};
self.block = block;
[self test];
}
- (void)test
{
self.block();
}
2015-11-11 13:49:59.540 俩个在不同文件中相同名称函数会冲突[1752:140238] 之前 - 0x7f83db43b9e0 (
1,
2
)
2015-11-11 13:49:59.541 俩个在不同文件中相同名称函数会冲突[1752:140238] 之后 - 0x7f83db43b9e0 (
1,
2
)
从这个例子中可以看出,block持有了对象(引用计数+1)。因为,array是局部变量,所以viewDidLoad执行完后该变量会被释放,由于默认局部变量是__strong,变量释放的同时对象引用计数-1,但是例子中看到在test方法中数组对象没有“死”,所以可以推测block对其引用计数+1。
总结 :block只会对外部局部变量进行浅拷贝,并在体内使用该拷贝的指针,不会拷贝对象本身,但是会持有对象(引用计数+1)。也就是说除非对象本身改变了,否则外部局部变量的改变是影响不到block体内的。这也就是为什么block叫做闭包,屏蔽外界对内的影响。
之前一直说的是局部变量,如果是全局变量(成员变量)进入block会怎么样呢?
例子6:
_array = [NSMutableArray arrayWithObjects:@"1",@"2", nil];
NSLog(@"之前 - %p %@", &_array, _array);
void (^block)() = ^{
NSLog(@"之后 - %p %@", &_array, _array);
};
block();
2015-11-11 14:06:00.699 俩个在不同文件中相同名称函数会冲突[1819:147698] 之前 - 0x7ffb8a5a9068 (
1,
2
)
2015-11-11 14:06:00.700 俩个在不同文件中相同名称函数会冲突[1819:147698] 之后 - 0x7ffb8a5a9068 (
1,
2
)
地址一样,所以block体的内外全局变量是同一个。这也提醒以后在block中使用全局变量,要注意全局变量会破坏block的封闭性(全局变量的改变会影响到block体内的运算)。