Block循环引用

参考文章http://www.cnblogs.com/eagley/p/5434485.html

Block循环引用问题研究  自从苹果在objc中添加Block功能支持以后已经过了很久。目前网上对于Block的使用有很多介绍。不过对于Block的内存管理问题,则是众说纷纭。再加上objc开始使用ARC以后,对于Block的内存管理又有了新的变化。因此在本文中笔者将根据自己的理解梳理一下Block的内存管理问题。1.Block简单原理  首先Block的原理要说起来还是挺简单的,就是将一个函数本身当成参数进行传递。而Block的优势就在于它不止可以访问自己函数作用域内的数据,它也可以访问自己作用域范围外的数据。当然,这也是Block内存管理出现困扰的源头。  当然,即使Block的内存管理需要特别关注。但是从工程框架来说,Block确实有存在的必要。比如在使用Block之前,当我们在一个对象(A)中需要另一个对象(B)给出解决方案的时候,我们通常会用代理的方式在A中将需要的参数传递给B,然后等待B提供的解决方案处理完以后再继续后续操作。复制代码ObjA.h@protocol ObjADelegate- (NSInteger)doSomething:(NSInteger)value;@end@interface ObjA {    __weak id_delegate;}@property (nonatomic, weak) iddelegate;@endObjA.m@implement ObjA@synthesize delegate = _delegate;- (void)function {    NSInteger value = 100;    if ([_delegate respondsToSelector:@selector(doSomethings:)]) {      value =  [_delegate doSomethings:value];    }    NSLog(@"value: %zd", value);}@endObjB.h@interface ObjB@endObjB.m@implement ObjB#pragma mark - ObjADelegate- (NSInteger)doSomething:(NSInteger)value {    return value + 100;}@end复制代码  事实上在仅仅只有一个代理的时候,Block并不见得比代理方便。但是当一个对象成为了多个代理的实现对象的时候,就会使得这个对象的代码变的非常臃肿,也很难以管理。比如下面这个类,光是头文件就能把人看晕了:@interface MKModelPagesViewController : UIViewController在有多个代理的情况下,使用Block方式就可以使得代码不再那么臃肿:

复制代码

ObjA.h

@interface ObjA

@property (copy, nonatomic) NSInteger (^doSomethings)(NSInteger value);

@end

ObjA.m

@implement ObjA

- (void)function {

NSInteger value = 100;

if (self.doSomethings) {

value = self.doSomethings(value);

}

NSLog(@"value: %zd", value);

}

@end

ObjB.h

@interface ObjB

@end

ObjB.m

@implement ObjB

- (void)anotherFunction {

ObjA* a = [ObjA new];

a.doSomethings = ^(NSInteger value) {

return value + 100;

};

}

@end

复制代码

可以看到,使用Block以后代码的结构比使用代理时候要更清晰。当然考虑到根据项目复杂程度,对象之间的通信频率的高低,我们可以按照自己的喜好选择使用Block还是代理。

2.Block内存管理

在苹果使用ARC管理之前,Block的内存管理需要区分是Global(全局)、Stack(栈)还是Heap(堆)。而在使用了ARC之后,苹果自动会将所有原本应该放在栈中的Block全部放到堆中,所以这使得我们现在的讨论可以省去很大一部分的麻烦。下面我们就只讨论ARC环境下全局Block和堆Block的内存管理。

首先,全局的Block比较简单,一句话就可以讲完:凡是没有引用到Block作用域外面的参数的Block都会放到全局内存块中,在全局内存块的Block不用考虑内存管理问题。(放在全局内存块是为了在之后再次调用该Block时能快速反应,当然没有调用外部参数的Block根本不会出现内存管理问题)。

所以Block的内存管理出现问题的,绝大部分都是在堆内存中的Block出现了问题。实际上属于Block特有的内存管理问题就只有一个:循环引用。

循环引用

Block的循环引用是比较容易被忽视,原本也是相对比较难检查出来的问题。当然现在苹果在XCode编译的层级就已经做了循环引用的检查,所以这个问题的检查就突然变的没有难度了。

简单说一下循环引用出现的原理:Block的拥有者在Block作用域内部又引用了自己,因此导致了Block的拥有者永远无法释放内存,就出现了循环引用的内存泄漏。下面举个例子说明一下:

复制代码

@interface ObjTest () {

NSInteger testValue;

}

@property (copy, nonatomic) void (^block)();

@end

@implement ObjTest

- (void)function {

self.block = ^() {

self.testValue = 100;

};

}

@end

复制代码

在这个例子中,ObjTest拥有了一个名字叫block的Block对象;然后在这个Block中,又对ObjTest的一个成员变量testValue进行了赋值。于是就产生了循环引用:ObjTest->block->ObjTest。

要避免循环引用的关键就在于破坏这个闭合的环。在目前只考虑ARC环境的情况下,笔者所知的只有一种方法可以破坏这个环:在Block内部对拥有者使用弱引用。

复制代码

@interface ObjTest () {

NSInteger testValue;

}

@property (copy, nonatomic) void (^block)();

@end

@implement ObjTest

- (void)function {

__weak ObjTest* weakSelf = self;

self.block = ^() {

weakSelf.testValue = 100;

};

}

@end

复制代码

请注意这两段代码中唯二的差别(加粗的代码段)。在Block外创建一个对于self的弱引用,然后在Block内引用self的地方全部使用这个弱引用。这样就使得Block内部不会对self本身做引用计数+1的操作。那样就可以打破循环引用的环了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,529评论 5 475
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,015评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,409评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,385评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,387评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,466评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,880评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,528评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,727评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,528评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,602评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,302评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,873评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,890评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,132评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,777评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,310评论 2 342

推荐阅读更多精彩内容