iOS循环引用的三种场景

来源:互联网 发布:dota2第一滴血数据bug 编辑:程序博客网 时间:2024/05/24 01:35

(1)计时器NSTimer

  • 若timer一直处于validate的状态,则其引用计数将始终大于0。
  • NSTimer经常会被作为某个类的成员变量,而NSTimer初始化时要指定self为target,容易造成循环引用。

先看一段NSTimer使用的例子(ARC模式):

 #import <Foundation/Foundation.h> @interface Friend : NSObject - (void)cleanTimer; @end #import "Friend.h"  @interface Friend ()  {      NSTimer *_timer;  }  @end  @implementation Friend  - (id)init {     if (self = [super init]) {         _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(handleTimer:)                                           userInfo:nil repeats:YES];     }     return  self; } - (void)handleTimer:(id)sender {     NSLog(@"%@ say: Hi!", [self class]); } - (void)cleanTimer {     [_timer invalidate];     _timer = nil; } - (void)dealloc {     [self cleanTimer];     NSLog(@"[Friend class] is dealloced"); }

在类外部初始化一个Friend对象,并延迟5秒后将friend释放(外部运行在非arc环境下)

Friend *f = [[Friend alloc] init];        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, *NSEC_PER_SEC), dispatch_get_main_queue(), ^{             [f release];         });

我们所期待的结果是,初始化5秒后,f对象被release,f的dealloc方法被调用,在dealloc里面timer失效,对象被析构。但结果却是如此:

2015-03-18 18:00:35.300 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:36.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:37.300 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:38.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:39.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!//运行了5次后没按照预想的停下来2015-03-18 18:00:40.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:41.300 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:42.300 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:43.299 WZLCodeLibrary[41422:3390529] Friend say: Hi!2015-03-18 18:00:44.300 WZLCodeLibrary[41422:3390529] Friend say: Hi!<br>.......根本停不下来.....

这是为什么呢?

  • timer持有Friend对象,而此时timer不停止计时就不会释放,它继续持有Friend对象,Friend对象就不会进入dealloc;

一个比较好的解决方法是开放这个函数,让Friend的调用者显式地调用来清理现场。如下:

Friend *f = [[Friend alloc] init];dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 5*NSEC_PER_SEC), dispatch_get_main_queue(), ^{    [f cleanTimer];    [f release];});

(2)block

block在copy时都会对block内部用到的对象进行强引用(ARC)或者retainCount增1(非ARC)。在ARC与非ARC环境下对block使用不当都会引起循环引用问题,一般表现为:

  • 某个类将block作为自己的属性变量,然后该类在block的方法体里面又使用了该类本身.
#import "Friend.h"@interface Friend ()@property (nonatomic) NSArray *arr;@end@implementation Friend- (id)init{    if (self = [super init]) {         self.arr = @[@111, @222, @333];        self.block = ^(NSString *name){            NSLog(@"arr:%@", self.arr);             NSLog(@"arr:%@", _arr);        };    }    return  self;}

解决方法:

__weak typeof(self) weakSelf = self;self.blkA = ^{//加一下强引用,避免weakSelf被释放掉__strong typeof(weakSelf) strongSelf = weakSelf;//不会导致循环引用.NSLog(@"%@", strongSelf->_xxView); };

(3)委托delegate

  • 声明delegate时手贱玩用了retain或者strong。

    解决方法:

    • 声明delegate时请用assign(MRC)或者weak(ARC)

    【原文】iOS容易造成循环引用的三种场景,就在你我身边!

0 0
原创粉丝点击