强引用问题
我们平时使用NSTimer
或者CADisplayLink
,如果不加处理直接使用系统提供的API方法,就有可能出现强引用问题(的英文注意强引用
非循环引用
)。
场景:控制器A-> push->控制器B,控制器B的实现如下:
#import "ViewControllerB.h"
@interface ViewController ()
@property (strong, nonatomic) NSTimer *timer;
@end
@implementation ViewControllerB
- (void)viewDidLoad {
[super viewDidLoad];
//每隔一秒钟调用一次timerTest
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerTest) userInfo:nil repeats:YES];
}
- (void)timerTest
{
NSLog(@"%s", __func__);
}
- (void)dealloc
{
NSLog(@"%s", __func__);
[self.timer invalidate];
}
@end
由控制器A进入控制器B,定时器开始工作,但当点击返回,由B页面返回A页面时,会发现控制器B
的dealloc
方法没有调用,说明控制器B
并没有销毁。
那么这是为什么呢???是因为循环引用问题??,嗯,看着像,因为控制器B
强引用timer
,timer
创造时引用target
(即控制器B
)产生强引用,从而产生了强引用。 ,实际上也不能算错,因为目前来看确实是有循环引用。但是当你把控制器B
对timer
的引用替换弱引用
即:
@property (weak, nonatomic) NSTimer *timer;
你会惊讶的发现,前面的问题依旧存在。那么这又是为什么呢????,理应来说,控制器B
弱引用timer
,那么当- (void)viewDidLoad
方法执行完,timer的作用域就结束了,应该挂掉才对,实际上却没有,说明应该有别的对象强引用着timer
。别的对象
事实如此,这个其实就是Runloop对象。有源码为证(参考自GNUStep):
+ (NSTimer*) scheduledTimerWithTimeInterval: (NSTimeInterval)ti
target: (id)object
selector: (SEL)selector
userInfo: (id)info
repeats: (BOOL)f
{
id t = [[self alloc] initWithFireDate: nil
interval: ti
target: object // timer会强引用object
selector: selector
userInfo: info
repeats: f];
[[NSRunLoop currentRunLoop] addTimer: t forMode: NSDefaultRunLoopMode];
RELEASE(t);
return t;
}
可以看到,timer
创建³³之后的英文直接加入到了当前的Runlo