美文网首页
解决NSTimer,强引用target引起的无法释放

解决NSTimer,强引用target引起的无法释放

作者: 31313_iOS | 来源:发表于2020-07-02 09:30 被阅读0次

    NSTimer 是iOS开发中常用的定时器,使用过程中也有一些弊端:

    • 1. 受到RunLoopde影响出现计时准确性问题(很多大佬都转而使用GCD计时器)
    • 2. 对target的强引用而导致页面无法释放的问题。

    本文分别提供了Swift和Object-C两中开发语言的解决办法。

    一、 先补充一下RunLoopMode

    1.开发者常用的模式

    NSDefaultRunLoopMode: 默认模式,通常主线程是在这个Mode下运行的
    UITrackingRunLoopMode:界面跟踪Mode,用于ScrollView追踪触摸滑动,保证界面滑动的时候不受其他Mode的影响
    kCFRunLoopCommonModes :这是一个占位用的Mode,不是一种真正的Mode, 默认包含NSDefaultRunLoopMode、NSModalPanelRunLoopMode、NSEventTrackingRunLoopMode这三个模式。(计时器我们主要使用这个模式)

    2.开发者几乎不用的模式

    UIInitalizationRunLoopMode: 在刚启动App时进入的第一个Mode,启动完成以后就不再使用
    GSEventReceiveRunLoopMode :接受系统事件的内部Mode,通常用不到

    通常情况下,我们会把Timer加入到Runloop中启用:

      [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    

    二、 解决NSTimer强引用target无法释放

    1.更换方法,使用block回调。

    使用系统iOS10及以后提供了一个使用block回调,可以避免target无法释放的计时器方法:
    (1)、swift版的方法:

     @available(iOS 10.0, *)
        public /*not inherited*/ init(timeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void)
    
    

    (2)、oc版的方法:

    + (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
    
    2.使用一个中间介来做消息转发。(推荐

    (1)、swift版的方法:

    import Foundation
    
    class TimerTarget: NSObject {
    
        weak var target :AnyObject?
        
        convenience init(_ target:AnyObject) {
            self.init()
            self.target = target
        }
        
        override func forwardingTarget(for aSelector: Selector!) -> Any? {
            return target
        }
        
        deinit {
            print("----  deinit ---")
        }
    }
    
    

    具体使用:

    func initTimer()  {
         timer = Timer(timeInterval: 1.0, target: TimerTarget(self), selector: #selector(timerStarted), userInfo: nil, repeats: true)
            RunLoop.current.add(timer!, forMode: .common)
    }
    

    (2)、oc版的方法:

    oc版本使用借助NSProxy这个抽象类,相比NSObject,NSProxy更轻量级, 做消息转发效率更高.必须继承实例化其子类才能使用。

    TimerProxy.h文件

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface TimerProxy : NSProxy
    
    - (instancetype)initWithTarget:(id)target;
    
    + (instancetype)proxyWithTarget:(id)target;
    
    @end
    
    NS_ASSUME_NONNULL_END
    

    TimerProxy.m文件

    #import "TimerProxy.h"
    
    @interface TimerProxy ()
    
    @property (nonatomic, weak) id target;
    
    @end
    
    @implementation TimerProxy
    
    - (instancetype)initWithTarget:(id)target {
        _target = target;
        return self;
    }
    
    + (instancetype)proxyWithTarget:(id)target {
        return [[TimerProxy alloc] initWithTarget:target];
    }
    
    - (id)forwardingTargetForSelector:(SEL)selector {
        return _target;
    }
    
    @end
    

    具体使用:

    - (void)initTimer {
        TimerProxy * proxy = [TimerProxy proxyWithTarget:self];
        self.timer = [NSTimer timerWithTimeInterval:1.0 target:proxy selector:@selector(timerStart) userInfo:nil repeats:YES];//NSRunLoopCommonModes
        [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
    }
    

    亲测有效,已结束!!!

    相关文章

      网友评论

          本文标题:解决NSTimer,强引用target引起的无法释放

          本文链接:https://www.haomeiwen.com/subject/zhehqktx.html