1、timer的释放问题
日常使用timer的过程中如果用到了下述方法声明可能会引起循环引用的问题
- (void)setupTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction) userInfo:nil repeats:YES];
}
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
}
由于self强引用了timer,同时timer也强引用了self,所以循环引用造成 dealloc 方法根本不会执行。这个时候self和timer都不会释放,造成内存泄漏
2、解决方案
2.1、分类实现
这里借用YY大佬的实现展示
@implementation NSTimer (YYAdd)
+ (void)_yy_ExecBlock:(NSTimer *)timer {
if ([timer userInfo]) {
void (^block)(NSTimer *timer) = (void (^)(NSTimer *timer))[timer userInfo];
block(timer);
}
}
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats {
return [NSTimer scheduledTimerWithTimeInterval:seconds target:self selector:@selector(_yy_ExecBlock:) userInfo:[block copy] repeats:repeats];
}
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)seconds block:(void (^)(NSTimer *timer))block repeats:(BOOL)repeats {
return [NSTimer timerWithTimeInterval:seconds target:self selector:@selector(_yy_ExecBlock:) userInfo:[block copy] repeats:repeats];
}
@end
个人觉得之所以需要多这一步主要是因为下边两个方法都要iOS10以后才可以使用
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
2.2、中间件的实现
这种方法来自网络,本人并没有用在项目中,因为感觉多此一举,没有必要。
这种实现的思路是添加一个中间件,通过弱引用解决相互引用的问题。
@interface HXHWeakObject()
@property (weak, nonatomic) id weakObject;
@end
@implementation HXHWeakObject
- (instancetype)initWithWeakObject:(id)obj {
_weakObject = obj;
return self;
}
+ (instancetype)proxyWithWeakObject:(id)obj {
return [[HXHWeakObject alloc] initWithWeakObject:obj];
}
/**
* 消息转发,让_weakObject响应事件
*/
- (id)forwardingTargetForSelector:(SEL)aSelector {
return _weakObject;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
void *null = NULL;
[invocation setReturnValue:&null];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
return [_weakObject respondsToSelector:aSelector];
}
具体使用
// target要设置成weakObj,实际响应事件的是self
HXHWeakObject *weakObj = [HXHWeakObject proxyWithWeakObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakObj selector:@selector(changeText) userInfo:nil repeats:YES];
一些其他问题
1、为什么用weak修饰NSTimer实例不行呢?
因为timer被加到runloop里面了