标签:
2.3.5 __block变量存储域
Block变量从栈复制到堆时对__block变量产生的影响
__block变量存储域
|
影响
|
栈
|
从栈复制到堆并被Block持有
|
堆
|
被Block持有
|
在多个Block中使用__block变量时,第一个Block从栈复制到堆时,__block变量也一并从栈复制到堆。在之后的Block从栈复制到堆时,__block变量被Block持有,__block变量引用计数增加。之前__block变量结构中的__forwarding能够做到“不管__block变量在栈上还是堆上都能访问”,原因是栈上的__block变量在复制到堆上时,他的成员变量__forwarding的值替换为复制后堆上的__block变量的地址。
2.3.6 截获对象
看下面代码:
typedef
void (^aBlock)(
id);
aBlock
ablock;
{
id
array=[[NSMutableArrayalloc]init];
ablock=^(id
objct){
[array addObject:objct];
NSLog(@"array count %d",[arraycount]);
};
}
ablock([[NSObjectalloc]init]);
ablock([[NSObjectalloc]init]);
|
作者写到:“执行该代码后,程序会强制结束,array随变量作用域的结束而被废弃”。但在我的实际运行中,array对象正常使用,程序也正常运行(Xcode5.1.1和Xcode6.1)。所以可能苹果已经修改这方面的机制,作者关于截获对象的结论不再列出,使用时截获对象的规则可参照截获自动变量的规则。
这一节提到_Block_object_assign函数和_Block_objec_dispose函数,前者相当于retain方法,将对象赋值在对象类型的结构体成员变量中(例如上面的代码中将一个对象加入到array中);后者相当于release方法,释放赋值在对象类型的结构体成员变量中的对象(如从array中释放某个object)。
以下情况Block会从栈复制到堆:
- 调用Block的copy实例方法
- Block作为函数返回值
- 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
- 在方法名含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时。
除以下情景外,推荐调用Block的copy实例方法(即如果系统已自动调用,我么就不需调用):
- Block作为函数值返回时
- 将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时
- 在方法名含有usingBlock的Cocoa框架方法或Grand Central Dispatch的API中传递Block时。
2.3.7 __block 对象和变量
此节沿用上节结论,略过
2.3.8 Block循环引用
Person.m文件代码
主程序代码:
{
...
Person *per=[[Personalloc]init];
NSLog(@"o");
...
}
|
执行结果是该源码中的dealloc方法未被调用.原因是,Person类的对象持有blk_,blk_中持有id类型变量self.并且由于Block语法赋值给成员变量blk_(此时为ARC模式,变量默认为__strong类型),所以栈上的Block复制到堆,并持有所使用的self,这就造成了循环引用。编译器也给出了警告。
为避免循环引用可使用__weak修饰变量,并将self赋值给变量使用。如下:
-(id)init{
self=[superinit];
id
__weak tmp=self;
blk_=^{
NSLog(@"self %@",tmp);
};
return
self;
}
|
在该代码中,由于当Block存在时,持有此Block的Person对象肯定存在(blk_属于Person对象),所以不需要对tmp进行判空。
在iOS4及更低版本中,没有ARC机制,使用__unsafe_unretained修饰符代替__weak修饰符。
下面的代码也会造成循环引用,因为Block语法内使用的obj属于对象的成员变量,想要截获对象的成员变量,自然也截获了对象本身self.
//arc打开状态
@interface
Person(){
void(^blk_)(void);
id
obj;
}
@end
@implementation
Person
-(void)dealloc{
NSLog(@"person dealloc");
}
-(id)init{
self=[superinit];
blk_=^{
NSLog(@"obj %@",obj);
};
return
self;
}
|
使用__block变量也能避免循环引用,如下代码:
//arc打开状态
@interface
Person(){
void(^blk_)(void);
}
@end
@implementation
Person
-(void)dealloc{
NSLog(@"person dealloc");
}
-(id)init{
self=[superinit];
__block
id tmp=self;
blk_=^{
NSLog(@"obj %@",tmp);
tmp=nil;
} ;
return
self;
}
-(void)execBlock{
blk_();
}
|
该代码没有引起循环引用,但是如果不调用execBlock这个方法,即不执行对tmp的赋值,就会引起循环引用。
- Person类对象持有Block;
- Block持有__block变量
- __block持有Person类对象
通过执行execBlock,__block变量tmp对Person类对象的强引用失效。
|
使用__block变量避免循环引用有如下优点,
- 通过__block变量可控制对象的持有时间
- 在不能使用__weak修饰符的环境下代替__unsafe_unretained修饰符,不必担心悬垂指针。
缺点是为避免循环引用必须执行Block。
2.3.9 copy/release
ARC无效时,需要手动将Block从栈复制到堆,用copy方法来复制,release方法来释放。只要Block位于堆上,则可以通过retain方法持有,对于栈上的Block调用retain方法不起任何作用。在c语言中也可以使Block,此时使用Block_copy和Block_release代替copy/release实例方法。另外,ARC无效时,__block说明符被用来避免Block循环引用,这是因为Block从栈复制到堆时,若Block使用的变量为附有__block说明符的id类型对象或对象类型自动变量,不会被retain,反之如果没有__block说明符则会被retain。
在ARC有效和无效时,__block说明符的用途大不一样,请注意。
Block部分到此结束。
[读书笔记]iOS与OS X多线程和内存管理 [Blocks部分-4]
标签:
原文地址:http://blog.csdn.net/zhaoyabei/article/details/44056211