----------------------------------------欢迎查看block连载博客:【专栏】---------------------------------------
【block编程第一篇】block语法 【block编程第二篇】block捕获变量和对象(当前)
【block编程第三篇】block的内存管理。 【block编程第四篇】block内部实现;
【block编程第五篇】block中如何避免循环引用
------------------------------------------------------------------------------------------------------------------------------
下面介绍,block捕获自动变量和对象
一、捕获自动变量值
首先看一个经典block面试题:
int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
val = 2;
blk();
上面这段代码,输出值是:val = 10.而不是2.
block 在实现时就会对它引用到的它所在方法中定义的栈变量进行一次只读拷贝,然后在 block 块内使用该只读拷贝;换句话说block截获自动变量的瞬时值;或者block捕获的是自动变量的副本。(我更喜欢这么理解:block捕获变量的机制更像是函数按值传递)
由于block捕获了自动变量的瞬时值,所以在执行block语法后,即使改写block中使用的自动变量的值也不会影响block执行时自动变量的值。深入了解block是如何实现捕获变量的,点击查看【block四篇】block的实现
所以,上面的面试题的结果是2不是10
__block说明符
前面讲过block所在函数中的,捕获自动变量。但是不能修改它,不然就是“编译错误”。但是可以改变全局变量、静态变量、全局静态变量。其实这两个特点不难理解:
● 不能修改自动变量的值是因为:block捕获的是自动变量的const值,名字一样,不能修改
● 可以修改静态变量的值:静态变量属于类的,不是某一个变量。由于block内部不用调用self指针。所以block可以调用。
解决block不能修改自动变量的值,这一问题的另外一个办法是使用__block修饰符。
__block int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
val = 2;
blk();
上面的代码,跟第一个代码段相比。只是多了一个__block修饰符。但是输出结果确是2了。
__block 变量的内部实现要复杂许多,__block 变量其实是一个结构体对象,拷贝的是指向该结构体对象的指针。点击这里查看【block四篇】block的实现
二、捕获OC对象
对于捕获ObjC对象,不同于基本类型;Block会引起对象的引用计数变化。
@interface MyClass : NSObject {
NSObject* _instanceObj;
}
@end
@implementation MyClass
NSObject* __globalObj = nil;
- (id) init {
if (self = [super init]) {
_instanceObj = [[NSObject alloc] init];
}
return self;
}
- (void) test {
static NSObject* __staticObj = nil;
__globalObj = [[NSObject alloc] init];
__staticObj = [[NSObject alloc] init];
NSObject* localObj = [[NSObject alloc] init];
__block NSObject* blockObj = [[NSObject alloc] init];
typedef void (^MyBlock)(void) ;
MyBlock aBlock = ^{
NSLog(@"%@", __globalObj);
NSLog(@"%@", __staticObj);
NSLog(@"%@", _instanceObj);
NSLog(@"%@", localObj);
NSLog(@"%@", blockObj);
};
aBlock = [[aBlock copy] autorelease];
aBlock();
NSLog(@"%d", [__globalObj retainCount]);
NSLog(@"%d", [__staticObj retainCount]);
NSLog(@"%d", [_instanceObj retainCount]);
NSLog(@"%d", [localObj retainCount]);
NSLog(@"%d", [blockObj retainCount]);
}
@end
int main(int argc, char *argv[]) {
@autoreleasepool {
MyClass* obj = [[[MyClass alloc] init] autorelease];
[obj test];
return 0;
}
}
执行结果为1 1 1 2 1。
__globalObj和__staticObj在内存中的位置是确定的,所以Block copy时不会retain对象。
_instanceObj在Block copy时也没有直接retain _instanceObj对象本身,但会retain self。所以在Block中可以直接读写_instanceObj变量。
localObj在Block copy时,系统自动retain对象,增加其引用计数。
blockObj在Block copy时也不会retain。