首页 > 其他 > 详细

NSTimer 循环引用的问题总结

时间:2020-06-30 17:01:29      阅读:108      评论:0      收藏:0      [点我收藏+]

原因:

self -> Timer -> target(self), 造成循环引用

导致不会调用dealloc 方法,内存泄漏

- (void)dealloc{
    [_timer invalidate];
    NSLog(@"********************delloc**************8");
}

 

解决方式:

1.API block的方式 iOS10以后可用

__weak typeof(self)weakself = self;
    _timer = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
        weakself.count++;
        NSLog(@"-------%ld",weakself.count);
    }];

 

2.手动破解循环

在适当的时机调用 (达到一定条件或者 例如在- (void)viewDidDisappear:(BOOL)animated {}调用,)

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

 

3. 借助runtime给对象添加消息处理的能力, 这种方式虽然能破解循环,但是 test 方法里获取到的self 是 _objct ,故感觉不太实用

//        借助runtime给对象添加消息处理的能力
    _objct = [[NSObject alloc] init];
    class_addMethod([_objct class], @selector(test), class_getMethodImplementation([self class], @selector(test)), "v@:");
    _timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(test) userInfo:@"ddd" repeats:YES];

 

4.消息转发机制

创建一个中间类 PHJProxy,


@interface PHJProxy : NSProxy

@property (nonatomic, weak) id target;

 

@end


@implementation
PHJProxy //方法1: //// 发送给target //- (void)forwardInvocation:(NSInvocation *)invocation { // [invocation invokeWithTarget:self.target]; //} //// 给target注册一个方法签名 //- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel { // return [self.target methodSignatureForSelector:sel]; //} // 方法2: //仅仅添加了weak类型的属性还不够,为了保证中间件能够响应外部self的事件,需要通过消息转发机制,让实际的响应target还是外部self,这一步至关重要,主要涉及到runtime的消息机制。 -(id)forwardingTargetForSelector:(SEL)aSelector { return self.target; } @end

这2种方式原理都是一样,使用消息的转发机制,把消息转发到 target 中

 

5. NSTimer 分类的方式,

@implementation NSTimer (LWTimer)
+ (NSTimer *)lw_timerWithTimeInterval:(NSTimeInterval)interval block:(void(^)())block repeats:(BOOL)repeats
{
    return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(timermethod:) userInfo:[block copy] repeats:YES];
}

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

上述创建方式调用者是NSTImer自己,只是NSTimer捕获了参数block。这样我们在使用timer时,由于target的改变,就不再有循环引用了。

使用方式:

__weak typeof(self)weakself = self;

_timer = [NSTimer lw_timerWithTimeInterval:2 block:^{
       weakself.count++;
       NSLog(@"-------%ld",weakself.count);
    } repeats:YES];

iOS10中,定时器的API新增了block方法,实现原理与此类似,这里采用分类为NSTimer添加了带有block参数的方法,而系统是在原始类中直接添加方法,最终的行为是一致的。

 

注意:

1、把timer改成弱引用

@property (nonatomic, weak) NSTimer *timer;
虽然self对timer是弱引用,但是控制的delloc方法的执行依赖于timer的invalidate,timer的invalidate又依赖于控制器的delloc方法,这是一个鸡生蛋还是蛋生鸡的问题,依旧是循环引用

2. 使用__weak 那换个思路能不能让NSTimer弱引用target

__weak typeof(self) weakSelf = self;

 self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(showMsg) userInfo:nil repeats:YES];

 

weak关键字适用于block,当block引用了块外的变量时,会根据修饰变量的关键字来决定是强引用还是弱引用,如果变量使用weak关键字修饰,那block会对变量进行弱引用,如果没有__weak关键字,那就是强引用。
  但是NSTimer的 scheduledTimerWithTimeInterval:target方法内部不会判断修饰target的关键字,所以这里传self 和 weakSelf是没区别的,其内部会对target进行强引用,还是会产生循环引用。

 

NSTimer 循环引用的问题总结

原文:https://www.cnblogs.com/liuwenqiang/p/13214275.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!