Block对捕捉变量的影响

来源:互联网 发布:js实现幻灯片效果 编辑:程序博客网 时间:2024/05/16 06:09

以前只知道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] 之前 - 0x7f8e11430fe02015-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] 之前 - 0x7fff5045c9182015-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体内的运算)。

0 0
原创粉丝点击