NSTimer

作者: MAXcrazs | 来源:发表于2021-04-29 19:49 被阅读0次

    Timer的模拟使用

    计时器一个很常用的场景就是,打开一个页面,页面中有一个banner使用计时器来自动翻页。在退出页面时,我们希望退出页面时,这个计时器能够停止工作,并且页面对象能够被销毁

    TimerObj

    // TimerObj.h
    @interface TimerObj : NSObject
    @property (nonatomic, strong, nullable) NSTimer *timer1;
    + (TimerObj *)object;
    
    - (void)printLog;
    - (void)printFinish;
    
    - (void)beginLog;
    - (void)beginLogWeak;
    
    @end
    
    @implementation TimerObj
    + (TimerObj *)object{
        return [[TimerObj alloc] init];
    }
    
    #pragma mark - log
    - (void)printLog{
        static int idx = 0;
        NSLog(@"msg: %d", ++idx);
    }
    - (void)printFinish{
        NSLog(@"msg finish!!");
    }
    
    - (void)dealloc {
        [self.timer1 invalidate];
        self.timer1 = nil;
        NSLog(@"%s", __func__);
    }
    @end
    

    Test

    // TimerObj
    - (void)beginLog{
        if (!self.timer1) {
            self.timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(printLog) userInfo:nil repeats:YES];
        }
        [self.timer1 fire];
    }
    
    void classTimerCommon () {
        @autoreleasepool {
            TimerObj *obj = [TimerObj object];
            [obj beginLog];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                [obj printFinish];
            });
        }
    }
    

    我们创建了TimerObj,调用- beginLog方法模拟view展示。同时模拟4秒后退出界面

    • 日志输出
     Hello, World!
     msg: 1
     msg: 2
     msg: 3
     msg: 4
     msg: 5
     msg finish!!
     msg: 6
     msg: 7
     msg: 8
    
    • 结论
      • 计时器并没有停止,obj也没有被销毁

    可能尝试使用weakSelf来结局问题,单理想很美满,显示却很惨

    // TimerObj
    - (void)beginLogWeak{
        if (!self.timer1) {
            __weak typeof(self) weakSelf = self;
            self.timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:weakSelf selector:@selector(printLog) userInfo:nil repeats:YES];
        }
        [self.timer1 fire];
    }
    
    void classTimerWeakSelf () {
        @autoreleasepool {
            TimerObj *obj = [TimerObj object];
            [obj beginLogWeak];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                [obj printFinish];
            });
        }
    }
    
    • 日志输出
     Hello, World!
     msg: 1
     msg: 2
     msg: 3
     msg: 4
     msg: 5
     msg finish!!
     msg: 6
     msg: 7
    

    使用一个对象来中转能够解决,但是却不那么优雅

    // TimerObjWeak
    @interface TimerObjWeak : NSObject
    @property (nonatomic, weak) TimerObj *weakObj;
    @end
    @implementation TimerObjWeak
    - (void)printOtherLog{
        [self.weakObj printLog];
    }
    - (void)dealloc {
        NSLog(@"%s", __func__);
    }
    @end
      
    // TimerObj
    - (void)beginLogOtherWeak{
        if (!self.timer1) {
            TimerObjWeak *otherObj = [[TimerObjWeak alloc] init];
            otherObj.weakObj = self;
            self.timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:otherObj selector:@selector(printOtherLog) userInfo:nil repeats:YES];
        }
        [self.timer1 fire];
    }
    
    void classTimerWeakOther () {
        @autoreleasepool {
            TimerObj *obj = [TimerObj object];
            [obj beginLogOtherWeak];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                [obj printFinish];
            });
        }
    }
    
    • 日志输出
     Hello, World!
     msg: 1
     msg: 2
     msg: 3
     msg: 4
     msg: 5
     msg finish!!
     -[TimerObjWeak dealloc]
     -[TimerObj dealloc]
    
    • 结论
      • 两个对象都进行了释放

    使用另一个对象来解决timer循环引用是可行的,但是不那么优雅。它引入了一个新的方法- printOtherLog。如果有多个类似TimerObj这样的对象,简直是可怕。使用NSProxy能非常优雅的解决这个问题

    NSProxy

    NSProxy很强大,这里只举例来解决Timer循环引用

    • TimerProxy
    @interface TimerProxy: NSProxy
    @property(nonatomic,weak)id target;
    @end
    
    @implementation TimerProxy
    -(instancetype)initWithTarget:(id)target{
        self.target = target;
        return self;
    }
    +(instancetype)proxyWithTarget:(id)target{
        return [[self alloc] initWithTarget:target];
    }
    -(NSMethodSignature *)methodSignatureForSelector:(SEL)sel{
        // 在具体的子类中重写此方法,为给定的选择器和代理对象所代表的类返回正确的对象
        return [self.target methodSignatureForSelector:sel];
    }
    -(void)forwardInvocation:(NSInvocation *)invocation{
        // 将给定的调用传递给代理表示的真实对象。
        SEL sel = invocation.selector;
        if ([self.target respondsToSelector:sel]) {
            [invocation invokeWithTarget:self.target];
        }
    }
    @end
    
    // 
    - (void)beginLogProxy{
        if (!self.timer1) {
            self.timer1 = [NSTimer scheduledTimerWithTimeInterval:1 target:[TimerProxy proxyWithTarget:self] selector:@selector(printLog) userInfo:nil repeats:YES];
        }
        [self.timer1 fire];
    }
    
    void classTimerProxy () {
        @autoreleasepool {
            TimerObj *obj = [TimerObj object];
            [obj beginLogProxy];
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 4*NSEC_PER_SEC), dispatch_get_main_queue(), ^{
                [obj printFinish];
            });
        }
    }
    
    • 日志输出
     Hello, World!
     msg: 1
     msg: 2
     msg: 3
     msg: 4
     msg: 5
     msg finish!!
     -[TimerObj dealloc]
    

    Timer的模式

    Apple的解释

    • NSRunLoopCommonModes

      Objects added to a run loop using this value as the mode are monitored by all run loop modes that have been declared as a member of the set of “common" modes; see the description of CFRunLoopAddCommonMode

    • NSDefaultRunLoopMode

      The mode to deal with input sources other than NSConnection

    • NSEventTrackingRunLoopMode

      A run loop should be set to this mode when tracking events modally, such as a mouse-dragging loop.

    • NSModalPanelRunLoopMode

      A run loop should be set to this mode when waiting for input from a modal panel, such as NSSavePanel or NSOpenPanel.

    • UITrackingRunLoopMode

      The mode set while tracking in controls takes place.

    相关文章

      网友评论

          本文标题:NSTimer

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