题记
在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 循环引用问题。暂时就这么多,其他方法希望留言补充。谢谢!
今天的文章NSTimer 循环引用问题分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/15771.html