前言
在我们开发的过程中,block运用的非常广泛,为了避免写过多的delegate或者是通知等,项目中会运用大量的block回调,虽然现在大部分的工程都是采用ARC,但是在ARC上面使用block更为复杂,在ARC机制下block自动的被copy到堆上(如果是在stack,或者是全局区是不会造成循环引用的),更具体的怎么个复杂情况,我在这里就不阐述了,大家可以去参考网络上的文章,已经写得很全面了;大家可以去参考学习他们的文章。
(一)本文主要描述在使用block回调过程中一个比较容易产生循环引用的场景,即在block中引用了对象本身的成员变量或者说属性;以下是我准备的一些方法,
#import "SuperObject.h"
#import <UIKit/UIKit.h>
@class SonObject;
typedef void(^CustomerBlock)();
typedef void(^CustomerBlockWithPara)(SonObject *son);
@interface SonObject : SuperObject
@property (nonatomic,copy) CustomerBlock blockTest;
@property (nonatomic,copy) NSString *age;
- (void)demo:(CustomerBlock)block;
- (void)demoPara:(CustomerBlockWithPara)paraBlock;
- (void)excuteBlock;
@end
#import "SonObject.h"
@interface SonObject ()
{
CustomerBlock customerBlock;
CustomerBlockWithPara customerBlockPara;
}
@end
@implementation SonObject
//+ (void)initialize
//{
// NSLog(@"SonObject 执行initialize %@",[self class]);
//}
//
//- (SonObject *)init
//{
// NSLog(@"SonObject 执行init %@",[self class]);
// return [super init];
//}
- (void)demo:(CustomerBlock)block
{
customerBlock = block;
// self.blockTest = block;
// //NSLog(@"block address -- %@",self.blockTest);
// NSLog(@"====demo=== %@",block);
//self.blockTest();
}
- (void)excuteBlock
{
self.age = @"20";
if (customerBlock) {
customerBlock();
}
if (customerBlockPara) {
customerBlockPara(self);
}
}
- (void)demoPara:(CustomerBlockWithPara)paraBlock
{
customerBlockPara = paraBlock;
}
- (void)dealloc
{
NSLog(@"dealloc");
}
@end
(二)制造一个循环引用的例子,使用了SonObject中的age属性。
//例子一
SonObject *son = [[SonObject alloc] init];
[son demo:^{
if ([son.age isEqualToString:@"20"]) {
NSLog(@"====%@",@"right");
}
}];
[son excuteBlock];
//例子二
son.blockTest = ^{
son.age = @"30";
};
例子一:son引用了customerBlock(是SonObject中的全局成员变量),然后在customerBlock中又引用了SonObject的age属性,因此造成了循环引用;(值得注意的是,我的编译器中竟然不提示Capturing 'demo' strongly in this block is likely to lead to a retain cycle),这让我很费解;最后SonObject中的dealloc函数并没有执行,即son对象并没有被释放掉。
例子二:比较明显,son引用了blockTest属性,blockTest属性又引用了son的age属性;(这回编译器提示了警告Capturing 'demo' strongly in this block is likely to lead to a retain cycle),why?为什么例子一的没有提示?我也在求解。
(三)消除以上的循环引用,让son这个对象得以释放掉;
- (void)testBlock
{
//方法一,创建一个指向son的弱引用对象
SonObject *son = [[SonObject alloc] init];
__weak typeof(son) weakSelf = son;
//NSLog(@"test address -- %@",_sonObject);
[son demo:^{
if ([weakSelf.age isEqualToString:@"20"]) {
NSLog(@"====%@",@"right");
}
}];
[son excuteBlock];
// //例子一
// SonObject *son = [[SonObject alloc] init];
//
// [son demo:^{
//
// if ([son.age isEqualToString:@"20"]) {
//
// NSLog(@"====%@",@"right");
// }
// }];
//
// [son excuteBlock];
//
// //例子二
// son.blockTest = ^{
//
// son.age = @"30";
// };
//方法二,执行回调函数时,将自身(self)当做参数回传到block,这样就像是编译器给我们做了弱引用操作;
son = [[SonObject alloc] init];
[son demoPara:^(SonObject *son) {
if ([son.age isEqualToString:@"20"]) {
NSLog(@"====%@",@"right");
}
}];
[son excuteBlock];
}
执行testBlock后,你会发现dealloc被成功的执行(2次),即每一次创建的对象都被成功释放掉了。
(四)总结
以上写的内容是我的个人的理解,也许有些地方理解错误了,也希望大家多多的指出来,大家互相的学习,block造成的循环引用太常见,避免它,以防内存泄漏。
来源:oschina
链接:https://my.oschina.net/u/1450995/blog/682975