nstimer循环引用_NSTimer 循环引用问题

题记

在iOS 10系统之前,系统的NSTimer是会引起循环引用的,导致内存泄漏。下面就针对这个问题给出几种解决方法。

在iOS 10以后系统,苹果针对NSTimer进行了优化,使用Block回调方式,解决了循环引用问题。

//API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));

[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {

// Do some things

}];

这个iOS 10方法能解决循环引用问题。但是我们目前大部分还是要适配iOS10以下系统。

平时我们都是这么使用NSTimer

[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(testTimer) userInfo:nil repeats:YES];

//或者

self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(testTimer) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSDefaultRunLoopMode];

- (void)testTimer {

NSLog(@"Do Some Things");

}

//停止Timer

- (void)dealloc {

[self.myTimer invalidate];

self.myTimer = nil;

}

但是上面你有没有发现, 这个dealloc 方法根本不会调用,只要程序没杀死,就不会执行。形成了大的死循环。因为self.timer 与 NSTimer 的target:self(VC) 相互强引用,没有释放,怎么可能执行dealloc。

五种方式解决NSTimer循环引用问题

方法一:

使用(void)didMoveToParentViewController:(UIViewController *)parent方法,在这个方法里清掉定时器,就会释放Timer对象,也就解决了强引用。即调用dealloc方法了。

注意:针对PresentVC 不适用。

//生命周期 移除childVC的时候

- (void)didMoveToParentViewController:(UIViewController *)parent {

if (parent == nil) {

[self.myTimer invalidate];

self.myTimer = nil;

}

}

运行结果

方法二 中间件方法

消息传递没有什么是中间件不能解决的,如果有,那就在加中间件 O(∩_∩)O哈哈~

//定义个中间件属性

@property (nonatomic, strong) id target;

_target = [NSObject new];

class_addMethod([_target class], @selector(testTimer), (IMP)timerIMP, "v@:");

//这里换成_target 不用self了。 这就没有了循环引用了

self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:_target selector:@selector(testTimer) userInfo:nil repeats:YES];

[[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSDefaultRunLoopMode];

void timerIMP(id self, SEL _cmd) {

NSLog(@"Do some things");

}

//停止Timer

- (void)dealloc {

[self.myTimer invalidate];

self.myTimer = nil;

NSLog(@"Timer dealloc");

}

此时也是可以的。

中间件方法

方法三:使用NSProxy类

新建一个类TimerProxy,继承NSProxy。

设置一个属性

//注意这里要使用weak

@property (nonatomic, weak) id target;

然后在实现两个方法:

/** 方法签名 */

- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {

return [self.target methodSignatureForSelector:sel];

}

/** 消息转发 */

- (void)forwardInvocation:(NSInvocation *)invocation {

[invocation invokeWithTarget:self.target];

}

在你需要的地方,然后导入TimerProxy头文件使用

@property (nonatomic, strong) TimerProxy *timerProxy;

_timerProxy = [TimerProxy alloc];//注意这里只有alloc方法

_timerProxy.target = self;

self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:_timerProxy selector:@selector(testTimer) userInfo:nil repeats:YES];

这时也可以解决循环引用问题。

方法四 仿照系统iOS 10 Block方法

新建一个NSTimer分类, QLTimer. 定义一个加方法

/** 定义一个加方法 */

+ (NSTimer *)QLscheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats block:(void(^)(void))timerBlock;

//实现方法

+(NSTimer *)QLscheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats block:(nonnull void (^)(void))timerBlock {

return [self scheduledTimerWithTimeInterval:timeInterval

target:self

selector:@selector(QLTimerHandle:)

userInfo:[timerBlock copy] //注意copy

repeats:repeats];

}

+(void)QLTimerHandle:(NSTimer *)timer {

void(^block)(void) = timer.userInfo;

if (block) {

block();

}

}

使用

__block typeof(self) weakSelf = self;

self.myTimer = [NSTimer QLscheduledTimerWithTimeInterval:1.0 repeats:YES block:^{

__block typeof(weakSelf) strongSelf = weakSelf;

[strongSelf testTimer];

}];

方法五

据说是经过苹果官方确认过的方法:used with GCD queues.

以上方法都可以解决NSTimer 循环引用问题。暂时就这么多,其他方法希望留言补充。谢谢!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值