NSTimer的运行机制

NSTimer的运行机制

NSTimer 是一个用于在 iOS 和 macOS 应用中定时执行任务的类。它的运行机制主要依赖于 run loop。下面详细解释 NSTimer 的运行机制:

NSTimer 的基本使用

创建一个 NSTimer 通常使用以下方法之一:

  1. 计划重复计时器:

    objective-c 复制代码
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
  2. 手动管理计时器:

    objective-c 复制代码
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

运行机制

  1. 创建计时器:

    当你创建一个 NSTimer 实例时,它会被初始化,但不会立即开始运行。创建时指定的时间间隔 timeInterval 决定了计时器触发的频率。

  2. 添加到 Run Loop:

    计时器必须被添加到一个 NSRunLoop 中才能开始运行。可以手动将计时器添加到 run loop 中,或者使用 scheduledTimer 方法自动将其添加到当前 run loop 中。

  3. Run Loop 监控:

    Run loop 会不断地监听其所管理的所有事件源,包括 NSTimer。当计时器的时间间隔到达时,run loop 会检测到这个事件,并触发相应的处理方法。

  4. 触发处理方法:

    当计时器触发时,指定的目标对象的选择器方法会被调用。在上面的例子中,当计时器触发时,timerFired: 方法会被执行。

  5. 重复计时器:

    如果计时器被设置为重复 (repeats:YES),它将在每次触发后自动重置并继续运行,直到被手动无效化 (invalidate)。

  6. 停止计时器:

    当你调用 invalidate 方法时,计时器会从 run loop 中移除并停止运行。计时器对象将不再有效并且无法再次使用。

注意事项

  • Run Loop 模式:

    计时器会运行在特定的 run loop 模式下。默认情况下,它们运行在 NSDefaultRunLoopMode 下。这意味着当 run loop 进入不同的模式(例如拖拽模式时),计时器可能不会触发。因此,有时需要将计时器添加到 NSRunLoopCommonModes 中,以确保它在多种模式下都能触发。

  • 准确性:
    NSTimer 的触发时间并不是精确的。它受到 run loop 负载和其他系统事件的影响,可能会有一些延迟。如果需要更高的精确性,可能需要考虑使用 CADisplayLinkdispatch_source_t 计时器。

示例代码

objective-c 复制代码
- (void)startTimer {
    // 创建一个每秒触发一次的计时器
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                  target:self
                                                selector:@selector(timerFired:)
                                                userInfo:nil
                                                 repeats:YES];
}

- (void)timerFired:(NSTimer *)timer {
    // 计时器触发时执行的代码
    NSLog(@"Timer fired!");
}

- (void)stopTimer {
    // 停止计时器
    [self.timer invalidate];
    self.timer = nil;
}

总结

NSTimer 是一个强大的工具,用于在应用中定时执行任务。它依赖于 run loop 运行,提供了简单且方便的定时功能,但在使用时需要注意其运行模式和准确性限制。


NSTimer默认被谁持有?

NSTimer 默认是由其所在的 run loop 持有的。具体而言,当你使用 scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 方法创建并调度一个 NSTimer 时,它会自动被添加到当前线程的默认 run loop 模式中,并被 run loop 强引用。

详细解释

  1. 自动调度计时器:

    当你使用 scheduledTimerWithTimeInterval:target:selector:userInfo:repeats: 方法创建一个计时器时,该计时器会立即被添加到当前线程的 run loop 中。

    objective-c 复制代码
    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];

    这种方法返回的计时器会被 run loop 强引用,因此你不需要手动持有它,计时器会自动开始工作。

  2. 手动添加计时器:

    如果你使用 timerWithTimeInterval:target:selector:userInfo:repeats: 方法创建计时器,你需要手动将其添加到一个 run loop 中:

    objective-c 复制代码
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerFired:) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];

    在这种情况下,只有当计时器被添加到 run loop 中时,run loop 才会持有该计时器。

持有关系示意图

plaintext 复制代码
-----------------------
|                     |
|    NSRunLoop        |
|      (strong)       |
|                     |
|    ---------------  |
|    |  NSTimer   |   |
|    ---------------  |
|                     |
-----------------------

计时器的生命周期

  1. 开始:
    • NSTimer 被添加到 run loop 时,run loop 会持有它,并在指定的时间间隔后触发。
  2. 执行:
    • 计时器触发后,会调用指定的目标对象的方法(selector)。
  3. 重复或停止:
    • 对于重复计时器,run loop 会继续持有它,直到计时器被手动无效化(invalidate)。
    • 对于非重复计时器,触发一次后计时器会自动无效化,并从 run loop 中移除。
  4. 无效化:
    • 调用 invalidate 方法会使计时器无效,run loop 会释放对计时器的持有。
    • 一旦无效化,计时器将不再触发,并且无法重新启用。

示例代码

objective-c 复制代码
- (void)startTimer {
    // 创建并调度一个计时器
    self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
                                                  target:self
                                                selector:@selector(timerFired:)
                                                userInfo:nil
                                                 repeats:YES];
}

- (void)timerFired:(NSTimer *)timer {
    // 计时器触发时执行的代码
    NSLog(@"Timer fired!");
}

- (void)stopTimer {
    // 停止计时器
    [self.timer invalidate];
    self.timer = nil;
}

总结

NSTimer 默认由其所在的 run loop 持有。这意味着只要计时器被正确添加到 run loop 中,你不需要额外持有它。记得在适当的时候无效化计时器以避免潜在的内存泄漏。

相关推荐
恋猫de小郭7 小时前
Compose Multiplatform 1.10 Interop views 新特性:Overlay 和 Autosizing
android·flutter·macos·kotlin·github·objective-c·cocoa
TheNextByte18 小时前
如何通过蓝牙将联系人从Android传输到 iPhone
android·cocoa·iphone
加油_Yeah8 小时前
Mac 科研/论文专用快捷键(Word + LaTeX + Finder) 与文件管理
macos·编辑器·word
ZhangBlossom8 小时前
Freqtrade 新人上手教程(macOS + Docker,无需 docker-compose)
macos·docker·容器
古城小栈8 小时前
Rust 交叉编译:MacOS ====> Linux (musl 静态编译)
linux·macos·rust
2501_915921438 小时前
360移动端性能监控实践QDAS-APM(iOS篇)
android·macos·ios·小程序·uni-app·cocoa·iphone
开开心心_Every1 天前
多端免费远程控制工具:4K流畅同账号直连
游戏·macos·微信·pdf·excel·语音识别·phpstorm
ha_lydms1 天前
在iCloud、Onedrive等云文件夹中开启超级右键菜单
macos·onedrive·icloud·超级右键·better365
我不是稻草人1 天前
Centos共享Mac文件
linux·macos·centos