NSTimer打破循环引用的几种方式

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];
    }];
 
原文地址:https://www.cnblogs.com/jiefangzhe/p/15119874.html