​timerWithTimeInterval​​创建出来的timer无法立刻使用,需要添加到NSRunloop中才可以正常工作, 相当于runloop强持有timer, timer又强持有self, 导致无法释放

一: 手动销毁定时器

但存在一些弊端, 比如push到下个页面时,当前页面仍在内存中,定时器仍在计时,来回关闭开启很麻烦

在viewWillDisappear 


[self.timer invalidate];
self.timer = nil;


或pop时在didMoveToParentViewController中


if (nil == parent) {
[self.timer invalidate];
self.timer = nil;
}


二: 自定义定时器, 添加target和selector, timer对self弱引用


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface TimerWrapper : NSObject

- (id) initWithTimerInterval:(NSTimeInterval)interval target:(id)target selector:(SEL) sel;

- (void) stop;

@end



#import "TimerWrapper.h"

@interface TimerWrapper()

@property (nonatomic, weak) id target;
@property (nonatomic, assign) SEL sel;
@property (nonatomic, strong) NSTimer* timer;

@end

@implementation TimerWrapper

- (id) initWithTimerInterval:(NSTimeInterval)interval target:(id)target selector:(SEL) sel {
if (self = [super init]) {
self.target = target;
self.sel = sel;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
}
return self;
}

- (void) fire {
if ([self.target respondsToSelector:self.sel]) {
[self.target performSelector:self.sel];
}
}

- (void) stop {
[self.timer invalidate];
self.timer = nil;
}


#import "TZViewController.h"
#import "TimerWrapper.h"

@interface TZViewController ()
//@property (nonatomic, strong) NSTimer* timer;
@property (nonatomic, strong) TimerWrapper* timer;

@end

@implementation TZViewController

- (void)viewDidLoad {
[super viewDidLoad];

/// RunLoop -> timer -> target --> self
self.timer = [[TimerWrapper alloc] initWithTimerInterval:1.0 target:self selector:@selector(fire)];
// self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(fire) userInfo:nil repeats:YES];
}


//- (void) didMoveToParentViewController:(UIViewController *)parent {
// if (nil == parent) {
// [self.timer invalidate];
// self.timer = nil;
// }
//}


- (void) fire {
NSLog(@"%s", __func__);
}

- (void)dealloc
{
[self.timer stop];
NSLog(@"%s", __func__);
}



三: 使用NSProxy, 借助一个虚基类NSProxy,(NSProxy其主要用来消息转发的处理, 自定义或者YYWeakProxy


#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface WeakProxy : NSProxy

@property (nonatomic, weak) id target;

@end

NS_ASSUME_NONNULL_END


#import "WeakProxy.h"

@implementation WeakProxy

//获取当前的方法签名
- (NSMethodSignature*) methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}


//指定当前消息的处理者
- (void) forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}

@end



#import "TZViewController.h"
#import "WeakProxy.h"

@interface TZViewController ()

//@property (nonatomic, strong) id target;
@property (nonatomic, strong) NSTimer* timer;
@property (nonatomic, strong) WeakProxy* proxy;


@end

@implementation TZViewController

- (void)viewDidLoad {
[super viewDidLoad];

//虚基类只有alloc方法,所以初始化,直接调用alloc
self.proxy = [WeakProxy alloc];
//当前Proxy的target设为当前的self,因为真正要处理消息的其实是当前的viewcontroller(其实这个target就相当于delegate)
self.proxy.target = self;

self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];
}


- (void) fire {
NSLog(@"%s", __func__);
}

- (void)dealloc
{
[self.timer invalidate];
self.timer = nil;
NSLog(@"%s", __func__);
}



@end



四: 引入中间者, 利用runtime, 动态添加方法, 将强引用转移到target上


#import "TZViewController.h"
#import <objc/runtime.h>

@interface TZViewController ()

@property (nonatomic, strong) id target;
@property (nonatomic, strong) NSTimer* timer;


@end

@implementation TZViewController

- (void)viewDidLoad {
[super viewDidLoad];

/// RunLoop -> timer -> target ---> self
self.target = [NSObject new];
class_addMethod([self.target class], @selector(fire), class_getMethodImplementation([self class], @selector(fire)), "v@:");


self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self.proxy selector:@selector(fire) userInfo:nil repeats:YES];
}


- (void) fire {
NSLog(@"%s", __func__);
}

- (void)dealloc
{
[self.timer invalidate];
self.timer = nil;
NSLog(@"%s", __func__);
}

@end



五: 系统高于10.0时, 使用带block的timer, 兼容10.0以下可给NSTimer分类添加方法


__weak typeof(self) weakSelf = self;
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0f repeats:YES block:^(NSTimer * _Nonnull timer) {
__strong typeof(self) strongSelf = weakSelf;
[strongSelf fire];
}];





首先我们创建一个NSTimer的分类,
// NSTimer+ZHTimer.h
#import <Foundation/Foundation.h>

@interface NSTimer (ZHTimer)
+(NSTimer *)zh_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(void))block;
@end

// NSTimer+ZHTimer.m
#import "NSTimer+ZHTimer.h"

@implementation NSTimer (ZHTimer)
+(NSTimer *)zh_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(void))block
{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(zh_blockHandle:) userInfo:block repeats:YES];

//这里面这个self,其实指的是当前的类对象,在内存中只存在一份,就是以单例的形式存在,所以我们在每一次创建实例变量都要通过这个类对象来创建,
//所以并不需要担心当前的target造成循环引用,因为单例不需要被释放,只有当APP被Q的时候,存在内存中的单例才会被释放掉。
}

+(void)zh_blockHandle:(NSTimer *)timer{
void(^block)(void) = timer.userInfo;
if (block) {
block();
}
}

@end

//调用
__weak typeof(self) weakSelf = self;
_timer = [NSTimer zh_scheduledTimerWithTimeInterval:1.0f repeats:YES block:^{
__strong typeof(self) strongSelf = weakSelf;
[strongSelf fire];
}];