美文网首页
iOS NSTimer

iOS NSTimer

作者: YANGXIXIYear | 来源:发表于2019-04-01 12:05 被阅读0次

    前段时间,做了一个视频播放的功能,用到了NSTimer,测试时,发现会出现在退出播放的界面或退到后台的时候,还会有播放的声音,也就是说定时器停止的功能失效,这里解析一下Timer无法释放(循环引用)的问题。

    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(timerMethod) userInfo:nil repeats:YES];
    
    - (void)dealloc {
        [_timer invalidate];
        _timer = nil;
    }
    

    如上代码,运行发现,并没有走dealloc方法,发生了内存泄漏,原因是self强引用Timer,而当前Timer也强引用target---self,那么要打破循环引用,有什么办法呢???

    方法1:在界面消失时释放定时器

    - (void)didMoveToParentViewController:(UIViewController *)parent {
        /**
         *  当从一个视图控制容器中添加或者移除viewController后,该方法被调用。
         *  parent:父视图控制器,如果没有父视图控制器,将为nil
         */
        if (!parent) {
            NSLog(@"Timer move to last");
            [_timer invalidate];
            _timer = nil;
     } 
    }
    

    方法2:引入中间者

    // 定义一个中间变量target
    _timerTarget = [NSObject new];
    /** Runtime 给target动态添加方法
     *   [_timerTarget class]: 表示给timerTarget所在的类添加方法
     *   @selector(timerMethod):表示添加的方法
     *   class_getMethodImplementation([self class], @selector(timerMethod)):表示方法的实现(函数 => 函数入口 =>  函数名)
    *    "v@:":方法类型(void用v来表示,id用参数@来表示,SEL用:来表示)
     */
    class_addMethod([_timerTarget class], @selector(timerMethod), class_getMethodImplementation([self class], @selector(timerMethod)), "v@:");
    // 定义timer,设置target
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target: _timerTarget selector:@selector(timerMethod) userInfo:nil repeats:YES];
    

    此时会正常走dealloc方法

    方法3:引入NSProxy,消息转发

    1> 创建一个继承自NSProxy的类:

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface CAProxy : NSProxy
    /**
     *  消息转发机制 真正的target
     */
    @property (nonatomic, weak) id target;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    #import "CAProxy.h"
    
    @implementation CAProxy
    
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
        /**
         *  找到方法签名
         */
        return [self.target methodSignatureForSelector:sel];
    }
    
    - (void)forwardInvocation:(NSInvocation *)invocation {
        /**
         *  设置 当前的消息执行者 为target
         */
        [invocation invokeWithTarget:self.target];
    
    @end
    

    2> 使用:

    _caProxy = [CAProxy alloc]; // 只有alloc 没有init方法
    _caProxy.target = self; // weak持有
    // target 设为_caProxy,而不是_caProxy.target
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0f target:_caProxy selector:@selector(timerMethod) userInfo:nil repeats:YES]; 
    

    3> 运行正常走dealloc方法

    方法4:iOS10.0后,用block完成

    __weak typeof(self) weakSelf = self;
    _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            __strong typeof(weakSelf) strongSelf = weakSelf;
            [strongSelf timerMethod];
        }];
    

    运行正常走dealloc方法

    方法5:参照方法4原生方法,给NSTimer添加分类,即可用于iOS10以前

    #import <Foundation/Foundation.h>
    
    NS_ASSUME_NONNULL_BEGIN
    
    @interface NSTimer (CACustomTimer)
    
    + (NSTimer *)ca_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
    
    @end
    
    NS_ASSUME_NONNULL_END
    
    #import "NSTimer+CACustomTimer.h"
    
    @implementation NSTimer (CACustomTimer)
    
    + (NSTimer *)ca_scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer * _Nonnull))block {
        
        return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(ca_blockHandle:) userInfo:block repeats:repeats];
    }
    
    + (void)ca_blockHandle:(NSTimer *)timer {
        void(^block)(void) = timer.userInfo;
        if (block) {
            block();
        }   
    }
    @end
    

    调用

    __weak typeof(self) weakSelf = self;
    _timer = [NSTimer ca_scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
            __strong typeof(weakSelf) strongSelf = weakSelf; 
            [strongSelf timerMethod];
        }];
    

    相关文章

      网友评论

          本文标题:iOS NSTimer

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