前言
NSTimer
是我们日常开发用的比较多的,但是由于我们在设置target
的时候通常都是传的self
;系统在target
内部会强引用当前的self
,而一旦我们的NSTimer
设置为成员变量或者属性的时候,那么timer
就是当前self
所持有,所以就导致了互相引用。
解决方案
1、在viewWillDisappear
里面销毁timer
(不推荐)
2、在didMoveToParentViewController
内部释放
3、引入中间类,并动态添加方法
4、二次包装NSTimer
5、使用NSTimer
的block
方法(iOS10
之后才能使用)
6、创建一个分类使用block
回调
7、用NSProxy
抽象类 (推荐使用)
一、在viewWillDisappear
里面销毁timer
- 在
viewWillAppear
中创建timer
- 在
viewWillDisappear
中销毁timer
二、在didMoveToParentViewController
内部释放
@property (nonatomic, strong) NSTimer *timer;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(__run) userInfo:nil repeats:YES];
- (void)didMoveToParentViewController:(UIViewController *)parent {
if (parent == nil) {
[self.timer invalidate];
self.timer = nil;
}
NSLog(@"-----release");
}
三、引入中间类,并动态添加方法
导入头文件#import
NSObject *obj = [[NSObject alloc] init];
Method method = class_getInstanceMethod(object_getClass(self), @selector(__run));
class_addMethod([obj class], @selector(__run), method_getImplementation(method), "v@:");
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:obj selector:@selector(__run) userInfo:nil repeats:YES];
四、二次包装NSTimer
LGSafeTimer
代码实现
@interface LGSafeTimer : NSObject
+ (LGSafeTimer *)lg_scheduledTimerWithTimeInterval:(NSTimeInterval)timerinterval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats;
- (void)lg_inValidate;
@end
#import "LGSafeTimer.h"
@interface LGSafeTimer ()
@property (nonatomic, weak) id target;
@property (nonatomic, strong) NSTimer *timer;
@property (nonatomic, assign) SEL selector;
@end
@implementation LGSafeTimer
+ (LGSafeTimer *)lg_scheduledTimerWithTimeInterval:(NSTimeInterval)timerinterval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats {
LGSafeTimer *timer = [[LGSafeTimer alloc] scheduledTimerWithTimeInterval:timerinterval
target:aTarget
selector:aSelector
userInfo:userInfo
repeats:repeats];
return timer;
}
- (instancetype)scheduledTimerWithTimeInterval:(NSTimeInterval)timerinterval
target:(id)aTarget
selector:(SEL)aSelector
userInfo:(id)userInfo
repeats:(BOOL)repeats {
if (self == [super init]) {
self.target = aTarget;
self.selector = aSelector;
self.timer = [NSTimer scheduledTimerWithTimeInterval:timerinterval
target:self
selector:@selector(lg_safeTimerRun)
userInfo:userInfo
repeats:repeats];
}
return self;
}
- (void)lg_inValidate {
[self.timer invalidate];
self.timer = nil;
}
- (void)dealloc {
NSLog(@"%@ --- %s", self, __func__);
}
#pragma mark - Private Methods
- (void)lg_safeTimerRun {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
if ([self.target respondsToSelector:self.selector]) {
[self.target performSelector:self.selector];
}
#pragma clang diagnostic pop
}
@end
使用:
在dealloc
函数内部需要释放timer
@property (nonatomic, strong) LGSafeTimer *safeTimer;
self.safeTimer = [LGSafeTimer lg_scheduledTimerWithTimeInterval:1 target:self selector:@selector(__run) userInfo:nil repeats:YES];
- (void)dealloc {
NSLog(@"--------%s------dealloc", __func__);
[self.safeTimer lg_inValidate];
}
五、创建一个分类使用block
回调
NSTimer+LGSafeTimer
实现
- 将定时器所执行的任务封装成
Block
,在调用定时器函数时,把block
作为userInfo
参数传进去 - 计时器现在的
target
是NSTimer
类对象,这是个单例,因此计时器是否会保留它,其实都无所谓。此处依然有保留环,然而因为类对象(class object)无需回收,所以不用担心
+ (NSTimer *)lg_scheduledTimerWithTimeInterval:(NSTimeInterval)timerinterval
repeats:(BOOL)repeats
block:(void (^)(void))block {
return [NSTimer scheduledTimerWithTimeInterval:timerinterval
target:self
selector:@selector(__fun:)
userInfo:[block copy]
repeats:repeats];
}
+ (void)__fun:(NSTimer *)timer {
void(^block)(void) = timer.userInfo;
if (block) {
block();
}
}
使用:
block
内部注意循环引用
self.timer = [NSTimer lg_scheduledTimerWithTimeInterval:1 repeats:YES block:^{
NSLog(@"---------");
}];
六、使用NSTimer
的内部API
- 10.0以后才能使用**
-
block
内部注意循环引用**
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"---------");
}];
七、NSProxy
抽象类
LGProxy
代码实现
@interface LGProxy : NSProxy
+ (instancetype)proxyWithObject:(id)object;
@end
#import "LGProxy.h"
@interface LGProxy ()
@property (nonatomic, weak) id object;
@end
@implementation LGProxy
+ (instancetype)proxyWithObject:(id)object {
LGProxy *proxy = [LGProxy alloc];
proxy.object = object;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
if (self.object) {
return [self.object methodSignatureForSelector:sel];
}
return nil;
}
- (void)forwardInvocation:(NSInvocation *)invocation {
if ([self.object respondsToSelector:invocation.selector]) {
[invocation invokeWithTarget:self.object];
}
}
@end
使用:
LGProxy *proxy = [LGProxy proxyWithObject:self];
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:proxy selector:@selector(__run) userInfo:nil repeats:YES];