iOS 如何使用 NSTimer 以及 runloop 和 NSTimer 的关系

这几天在研究 RunLoop,记录一下runloop 和 NSTimer 的关系,毕竟这个使我们比较常用的.

关于 runloop 参考资料很多, 我看的是 ibireme (郭耀源)的博客,国内开源大牛,程序员的楷模,他的博客链接


在 ios 系统中,每启动一个线程,都会跟一个对应的 runloop,runloop 默认是关闭的 需要我们手动获取,设置并启动,(详细参考上面的博客),主线程例外,系统自动为主线程启动一个 runloop 并配置完毕.这里我们不管,主要看unloop 和 NSTimer 的关系, 如何正确使用 NSTimer.


 NSTimer 的创建方法

        NSTimer *time1 = [NSTimer alloc]initWithFireDate:<#(nonnull NSDate *)#> interval:<#(NSTimeInterval)#> repeats:<#(BOOL)#> block:<#^(NSTimer * _Nonnull timer)block#>;
        
        NSTimer *timer2 = [NSTimer alloc]initWithFireDate:<#(nonnull NSDate *)#> interval:<#(NSTimeInterval)#> target:<#(nonnull id)#> selector:<#(nonnull SEL)#> userInfo:<#(nullable id)#> repeats:<#(BOOL)#>;
        
        NSTimer *timer3 = [NSTimer scheduledTimerWithTimeInterval:<#(NSTimeInterval)#> target:<#(nonnull id)#> selector:<#(nonnull SEL)#> userInfo:<#(nullable id)#> repeats:<#(BOOL)#>];
        
        NSTimer *timer4 = [NSTimer scheduledTimerWithTimeInterval:<#(NSTimeInterval)#> invocation:<#(nonnull NSInvocation *)#> repeats:<#(BOOL)#>];
        
        NSTimer *timer5 = [NSTimer scheduledTimerWithTimeInterval:<#(NSTimeInterval)#> repeats:<#(BOOL)#> block:<#^(NSTimer * _Nonnull timer)block#>];

首先所有的 timer 必须要加入 runloop 中才能生效.

然后使用 timer 要尤其注意释放时机,否则很容易导致内存泄露或者其他麻烦.

    /* 关于 timer1 在主线程中使用,
     * 在没有加入 runloop 的情况下, 可以使用 fire 立即执行一次, 但是对于设置的repeat:YES 就没有效果了,block 只会执行一次
     * 如果 repeat:NO 那么 timer 在执行一次之后会自动释放, [timer fire]之后,再次调用[timer fire] 就没有效果了
     * 再加入 runloop 的情况下, 把 timer 加入 runloop 就会执行. 因为主线程的 runloop 一直在运行,所以我们主要获取 runloop
     * 在加入定时器就好.  不需要调用 [runloop run]
     * 加入 runloop 之后,repeat:NO时,定时器执行一次就会释放当前 VC,repeat:YES 当前 VC 被持有,必须显示执行[_timer1 invalidate]
     * 释放 timer1才能释放 VC, 否则妥妥的内存泄露
     */
   self.timer1 = [[NSTimer alloc]initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 repeats:NO block:^(NSTimer * _Nonnull timer) {
            NSLog(@"执行 timer1 程序");
    }];
    //    [self.timer1 fire];

    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop addTimer:_timer1 forMode:(NSDefaultRunLoopMode)];
//                [runloop run];
//    [_timer1 invalidate];

    /* timer1 在子线程中使用
     *
     * 使用情况和在主线程中是一样的,要注意的是 加入当前 runloop 的时候,要启动 runloop, 因为子线程的 runloop 默认是关闭的
     *
     */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
    
        self.timer1 = [[NSTimer alloc]initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 repeats: YES block:^(NSTimer * _Nonnull timer) {
            NSLog(@"执行 timer1 程序");
        }];
        
        NSRunLoop *runloop = [NSRunLoop currentRunLoop];
        [runloop addTimer:_timer1 forMode:(NSDefaultRunLoopMode)];
         [runloop run];
//        [self.timer1 fire];
    });

    
    /* 关于 timer2 在主线程中使用,
     * 在没有加入 runloop 的情况下, 可以使用 fire 立即执行一次, 但是对于设置的repeat:YES 就没有效果了,timerAction 只会执行一次
     
     *You must add the new timer to a run loop, using addTimer:forMode:. Upon firing, the timer sends the message aSelector to target. (If the timer is configured to repeat, there is no need to subsequently re-add the timer to the run loop.)
     *但是官方推荐使用这个方法我们必须要加入 runloop ,其实正常使用确实是要你加入 runloop 的
     
     * 参数 repeat: If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.
     * 如果 repeat:NO 那么 timer 在执行一次之后会自动释放, [timer fire]之后,再次调用[timer fire] 就没有效果了
     
     * 参数 userInfo: Custom user info for the timer. The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter may be nil.
     * 该方法允许 timer 携带一个参数 userInfo(字典),里面可以包含我们想要携带的参数,可以为 nil,但是要注意的是 这个定时器对当前对应也是强持有
     
     * 再加入 runloop 的情况下, 把 timer 加入 runloop 就会执行. 因为主线程的 runloop 一直在运行,所以我们主要获取 runloop
     * 在加入定时器就好.  不需要调用 [runloop run]
     * 加入 runloop 之后,repeat:NO时,定时器执行一次就会释放当前 VC,repeat:YES 当前 VC 被持有,必须显示执行[_timer1 invalidate]
     * 释放 timer1才能释放 VC, 否则妥妥的内存泄露
     
     
     * 子线程使用 注意事项是一样的 参考一下timer1就知道了
     
     */
    self.timer2 = [[NSTimer alloc]initWithFireDate:[NSDate dateWithTimeIntervalSinceNow:1] interval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
        //self.timer2.userInfo
//        [self.timer2 fire];

    NSRunLoop *runloop = [NSRunLoop currentRunLoop];
    [runloop addTimer:_timer2 forMode:(NSDefaultRunLoopMode)];
//                [runloop run];
//    [_timer2 invalidate];

    
    /* 这三种方法 根上面的区别就是 创建 timer 并且自动加到当前的 runloop 当中. 所以在主线程里可以直接运行
     * 子线程中 子线程 runloo 启动就可以执行 timer
     * 注意事项 和 前面两个情况是一样的 repeat:YES 要注意合适的时机显示 失效 timer  否则注意内存泄露
     *
     * 这里注意下 NSInvocation 的用法, timer4的参数 就是这个
     *
     */
    
    
    self.timer3 = [NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
           NSLog(@"执行 timer3 程序");
    }];
    NSTimer *timer4 = [NSTimer scheduledTimerWithTimeInterval:1 invocation:nil repeats:YES];

    NSTimer *timer5 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
    
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
        dispatch_async(queue, ^{

            NSTimer *timer5 = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerAction:) userInfo:nil repeats:YES];
            // 自动加入当前的 runloop 只需要获取当前 runloop 并启动 即可
            NSRunLoop *runloop = [NSRunLoop currentRunLoop];
             [runloop run];
        });

注意 NSInvocation 的使用


NSInvocation 用法
    //NSInvocation;用来包装方法和对应的对象,它可以存储方法的名称,对应的对象,对应的参数,
    /*
     NSMethodSignature:签名:再创建NSMethodSignature的时候,必须传递一个签名对象,签名对象的作用:用于获取参数的个数和方法的返回值
     */
    //创建签名对象的时候不是使用NSMethodSignature这个类创建,而是方法属于谁就用谁来创建
    
    NSMethodSignature*signature = [SSSViewController instanceMethodSignatureForSelector:@selector(sendMessageWithNumber:WithContent:)];
    //1、创建NSInvocation对象
    NSInvocation*invocation = [NSInvocation invocationWithMethodSignature:signature];
    invocation.target = self;
    //invocation中的方法必须和签名中的方法一致。
    invocation.selector = @selector(sendMessageWithNumber:WithContent:);
    /*第一个参数:需要给指定方法传递的值
     第一个参数需要接收一个指针,也就是传递值的时候需要传递地址*/
    //第二个参数:需要给指定方法的第几个参数传值
    NSString*number = @"1111";
    //注意:设置参数的索引时不能从0开始,因为0已经被self占用,1已经被_cmd占用
    [invocation setArgument:&number atIndex:2];
    NSString*number2 = @"啊啊啊";
    [invocation setArgument:&number2 atIndex:3];
    //2、调用NSInvocation对象的invoke方法
    //只要调用invocation的invoke方法,就代表需要执行NSInvocation对象中制定对象的指定方法,并且传递指定的参数
//    [invocation invoke];
    
    
    
    /*
     * After ti seconds have elapsed, the timer fires, invoking invocation.
     * 当 timer到了执行点的时候 会 invoking invocation. 触发 invocation.里面的方法
     * 使用 invocation. 就可以执行 我们自定义的方法 ,携带多个自定义的参数
     */

    NSTimer *timer4 = [NSTimer scheduledTimerWithTimeInterval:1 invocation:(invocation) repeats:YES];



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值