防止NSTimer循环引用的几个方法

在iOS开发中我们经常会使用NSTimer,但是在使用过程中会遇到一个比较头疼的问题就是循环引用的问题,导致NSTimer所在的类不销毁,定时器也不会停止。

如何解决循环引用

导致循环引用的原因是:在创建NSTimer对象的时候需要指定一个target,而target我们一般直接使用self,当self强引用NSTimer对象就会导致循环引用。
所以解决办法就是:把循环引用的闭环断开。

针对这个问题的几种解决方案

方案一(推荐,使用方便)

NSTimer分类的实现:

@implementation NSTimer (XTTimer)

+ (NSTimer *)xt_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(void))block {
    return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(rw_timerBlockHandle:) userInfo:block repeats:repeats];
}

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

@end

使用:

- (void)createTimer1 {
    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer xt_scheduledTimerWithTimeInterval:1.0 repeats:YES block:^{
        [weakSelf doSomething];
    }];
}
  • 解释:timer的持有者强引用timer这个对象,而timer强引用的是NSTimer的类对象,没有造成循环引用。
  • 注意:在使用的时候block中使用self需要用__weak修饰,最后在dealloc方法里面调用[timer invalidate];

其实在iOS10以后,官方就已经提供了一个新的方法+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));这个方法就可以解决循环引用的问题,但是要iOS10以后才能使用。

方案二

- (void)createTimer2 {
    self.target = [NSObject new];
    class_addMethod([self.target class], @selector(doSomething), class_getMethodImplementation([self class], @selector(doSomething)), "v@:");
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self.target selector:@selector(doSomething) userInfo:nil repeats:YES];
}

实现思路:首先运用runtime给NSObject动态添加一个方法- (void)doSomething,并且这个方法实现的指针指向[self class]类中的- (void)doSomething,然后指定self.target这个对象为NSTimer的target即可。当定时器回调的时候就会调用- (void)doSomething方法。

方案三

@interface XTProxy : NSProxy
@property (nonatomic, weak) id target;
@end


@implementation XTProxy
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
    return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
    [invocation invokeWithTarget:self.target];
}
@end

使用:

- (void)createTimer3 {
    self.proxy = [XTProxy alloc];
    self.proxy.target = self;
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self.proxy selector:@selector(doSomething) userInfo:nil repeats:YES];
}

这个方案的实现方式主要是加入了一个中间者proxy,使得timer不直接持有self,而是持有proxy,让proxy对象弱引用self来解决循环引用。当定时器回调的时候,通过消息转发机制,把消息重定向给self。

NSProxy拓展

官方文档:

Typically, a message to a proxy is forwarded to the real object or causes the proxy to load (or transform itself into) the real object. Subclasses of NSProxy can be used to implement transparent distributed messaging (for example, NSDistantObject) or for lazy instantiation of objects that are expensive to create.

NSProxy implements the basic methods required of a root class, including those defined in the NSObject protocol. However, as an abstract class it doesn’t provide an initialization method, and it raises an exception upon receiving any message it doesn’t respond to. A concrete subclass must therefore provide an initialization or creation method and override the forwardInvocation: and methodSignatureForSelector: methods to handle messages that it doesn’t implement itself. A subclass’s implementation of forwardInvocation: should do whatever is needed to process the invocation, such as forwarding the invocation over the network or loading the real object and passing it the invocation. methodSignatureForSelector: is required to provide argument type information for a given message; a subclass’s implementation should be able to determine the argument types for the messages it needs to forward and should construct an NSMethodSignature object accordingly. See the NSDistantObject, NSInvocation, and NSMethodSignature class specifications for more information.

NSProxy是一个用来做消息转发的抽象类,使用时需写一个子类继承自NSProxy并且子类需要实现两个方法,- (void)forwardInvocation:(NSInvocation *)invocation;- (nullable NSMethodSignature *)methodSignatureForSelector:(SEL)sel
当NSProxy对象发送消息时,会跳过查找方法实现、动态方法解析、被援接受者几个步骤直接进行消息的重定向,所以相比较NSObject的消息转发而言,NSProxy减少了几个步骤,效率更高性能更优。


如有写得不对的地方或其他任何问题,欢迎私信或者留言一起交流。
欢迎大家点赞点关注,后续会持续更新文章~~~

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 场景:一个VC想引用一个带着NStimer的View.但是一般的情况下我们要在VC的dealloc中还要销毁Vie...
    图长伴阅读 3,865评论 0 1
  • 1.为什么要弱引用NSTimer2.如何弱引用https://blog.csdn.net/yohunl/artic...
    KevinChein阅读 3,265评论 0 0
  • NSTimer产生循环引用的原因 我们首先看下NSTimer的初始化方法 timerWithTimeInterva...
    Pusswzy阅读 5,822评论 0 6
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,802评论 1 32
  • 定时器的用法 系统提供了8个创建方法,6个类创建方法,2个实例初始化方法。有三个方法直接将timer添加到...
    gpylove阅读 5,768评论 1 3