在使用 block 的时候想修改外部的值,一般都是需要时用 __block 修饰才能修改,但是有一个些“可变集合类”属于例外和一些例外情况。
第一种情况 __block修饰非集合类
__block NSInteger number = 1;
Block block = ^(NSInteger a) {
number += a;
return number;
};
NSInteger b = block(5);
NSLog(@"b=%ld", b);
复制代码
输出的结果是
2017-04-20 09:33:24.709179+0800 Block[24319:2750379] b=6
复制代码
第二种情况 没有__block修饰的可变集合类
NSMutableArray *array1 = [NSMutableArray new];
[array1 addObject:@0];
Block block = ^(NSInteger a) {
[array1 addObject:@1];
return a;
};
NSInteger b = block(5);
NSLog(@"b=%ld", b);
NSLog(@"array=%@", array1);
复制代码
输出的结果是
2017-04-20 09:33:24.709179+0800 Block[24319:2750379] b=5
2017-04-20 09:33:24.709399+0800 Block[24319:2750379] array=(
0,
1
)
复制代码
第三种情况 __block修饰的可变集合类
__block NSMutableArray *array1 = [NSMutableArray new];
[array1 addObject:@0];
Block block = ^(NSInteger a) {
[array1 addObject:@1];
NSMutableArray *array2 = [NSMutableArray arrayWithObject:@2];
array1 = array2;
return a;
};
NSInteger b = block(5);
NSLog(@"b=%ld", b);
NSLog(@"array=%@", array1);
复制代码
输出的结果是
2017-04-20 09:33:24.709179+0800 Block[24319:2750379] b=5
2017-04-20 09:33:24.709399+0800 Block[24319:2750379] array=(
2
)
复制代码
第四种情况 没有__block修饰属于当前对象属性的可变集合类
self.array1 = [NSMutableArray new];
[self.array1 addObject:@0];
Block block = ^(NSInteger a) {
[array1 addObject:@1];
NSMutableArray *array2 = [NSMutableArray arrayWithObject:@2];
self.array1 = array2;
return a;
};
NSInteger b = block(5);
NSLog(@"b=%ld", b);
NSLog(@"array=%@", array1);
复制代码
输出的结果是
2017-04-20 09:33:24.709179+0800 Block[24319:2750379] b=5
2017-04-20 09:33:24.709399+0800 Block[24319:2750379] array=(
2
)
复制代码
总结
我们从上面的例子可以总结出来以下结果
__block
修饰的非集合、集合对象都可以赋值- 没有
__block
修饰的集合可以修改其中的对象 - 当前类属性没有
__block
修饰的时候也可以赋值
如果没有使用 __block
修饰会发生什么呢?
分析
知道结果之后,我们分析一下原因
1、为什么__block
修饰之后我们可以赋值呢?
使用 clang -rewrite-objc main.m
编译一下得到以下结果
- 没有使用
__block
修饰数组的情况 - 使用
__block
修饰数组的情况
对比之后我们可以发现(系统也给了注释)有没有 __block
的区别就是
- 拷贝值
- 拷贝指针
NSMutableArray *array = __cself->array1; // bound by copy
__Block_byref_array1_1 *array1 = __cself->array1; // bound by ref
复制代码
只有拷贝指针的才能执行赋值。
2、为什么当前类属性我们可以赋值呢?
同样编译一下可得
因为self.
调用了属性的set方法,使用runtime实现消息发送,不过可能会存在内存泄露。
不用self.
也可以实现赋值,
_array1 = array2;
复制代码
编译后是
(*(NSMutableArray **)((char *)self + OBJC_IVAR_$_Test$_array1)) = array2;
复制代码
3、为什么没有__block
的可变集合类我们可以修改其中的对象呢?
从 1 的第一张图我们可以看到 [array1 addObject:@1];
编译之后的是
((void (*)(id, SEL, ObjectType))(void *)objc_msgSend)((id)array1, sel_registerName("addObject:"), (id)((NSNumber *(*)(Class, SEL, int))(void *)objc_msgSend)(objc_getClass("NSNumber"), sel_registerName("numberWithInt:"), 1));
复制代码
同 2 调用的set方法,此处也是消息发送。
水平有限,如有错误还请指出~