前言
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 <objc/runtime.h>
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];