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

来源:互联网 发布:java权限系统思路 编辑:程序博客网 时间:2024/04/25 17:23

这几天在研究 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
原创粉丝点击