局部变量引起的循环引用

开发中写了个demo,具体片段如下,创建了三个局部对象Button,通过RAC做相应的按钮事件操作。没想到竟然发现到有内存泄漏的问题。因为已经排除了RAC导致的原因(具体ReactiveCocoa的内存管理可以看:http://blog.csdn.net/y_csdnblog_xx/article/details/51483111),所以刚开始时候也比较一头雾水。但是最后发现代码中犯了一个很低级的错误。

UIButton * btn0 = [[UIButton alloc]initWithFrame:CGRectMake(100, 450, 50, 100)];
[self.view addSubview:btn0];
UIButton * btn1 = [[UIButton alloc]initWithFrame:CGRectMake(200, 450, 50, 100)];
[self.view addSubview:btn1];
[[btn0 rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton * sender) {

//do something...

btn0.selected = YES;

btn1.selected = NO;

}];
[[btn1 rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton * sender) {

//do something...

btn0.selected = NO;

btn1.selected = YES;

}];

正常我们使用RAC或者Block,如果有全局属性在Block中调用并且该Block被本类对象持有,我们都会对self做一个弱引用,更保险一些的话还会在Block中再强引用一下这个指向self的弱引用指针。这个操作就能很有效避免循环引用带来的内存问题。

但是在以上代码中,我并没有创建全局属性并且也没有在Block中调用任何全局属性,结果同样出现了循环引用。我已经意识到了问题肯定出在Block中,就顺着代码排查可能性。我先把btn1的事件响应信号的接收Block中的btn0.selected = NO;去掉,发现问题解决了。因此意识到,即使是局部变量对象,如果存在同样的情况,也会导致循环引用。

具体说明:经过以上代码,有四个地方引用了btn0对象:1.btn0指针。2.self.view。3.btn0自身事件响应信号接收方法的block。4.btn1自身事件响应信号接收方法的block。同样有四个地方引用了btn1对象:1.btn1指针。2.self.view。3.btn1自身事件响应信号接收方法的block。4.btn0自身事件响应信号接收方法的block。这两按钮前三个情况都没问题,关键在于第四种情况。btn0的释放取决于btn1的释放,btn1同样要等btn0释放后才可释放其block进而才可释放。此时就出现了循环引用。

解决办法:给会导致循环引用的局部对象创建弱引用指针供相应Block中使用。

__weak UIButton * weakBtn0 = btn0;
__weak UIButton * weakBtn1 = btn1;

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.