You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift中RunLoop阻塞TableView问题:滚动时数值停止更新求助

问题根源分析

你碰到的这个情况本质是RunLoop模式切换导致的:当UITableView滚动时,主线程的RunLoop会切换到UITrackingRunLoopMode(iOS 10+后归属于NSRunLoopCommonModes下的跟踪模式),而默认创建的定时器只绑定在NSDefaultRunLoopMode下——这就导致滚动时定时器暂停触发,哪怕你尝试关联RunLoop,大概率是没加对模式。

下面给你几个针对性的解决方案,从修复现有代码到优化架构都有:


方案1:修复tableTimer的RunLoop绑定模式

如果你想继续用现有定时器方案,只需要把tableTimer加入NSRunLoopCommonModes,这样不管是默认模式还是滚动的跟踪模式,定时器都会正常触发:

Objective-C 代码示例

// 创建tableTimer后,替换原有的RunLoop绑定代码
[self.tableTimer addTimer:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

Swift 代码示例

// 创建tableTimer后执行
RunLoop.current.add(tableTimer, forMode: .common)

方案2:用CADisplayLink替代NSTimer(更适合UI刷新场景)

因为你是要实时更新UI,CADisplayLink是跟屏幕刷新率同步的,而且默认就会在Common Modes下运行,不仅能解决滚动暂停的问题,还能避免0.1秒定时器和屏幕帧率不同步导致的卡顿:

Objective-C 代码示例

// 在TableViewController的viewDidLoad里创建CADisplayLink
self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateVisibleCells)];
[self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];

// 对应的单元格刷新方法
- (void)updateVisibleCells {
    NSArray<UITableViewCell *> *visibleCells = self.tableView.visibleCells;
    for (UITableViewCell *cell in visibleCells) {
        // 从AppDelegate获取计数变量并更新UI
        AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
        cell.textLabel.text = [NSString stringWithFormat:@"%ld", (long)appDelegate.countVariable];
    }
}

// 记得在控制器销毁时停止displayLink
- (void)dealloc {
    [self.displayLink invalidate];
}

方案3:优化数据传递逻辑(彻底摆脱定时器轮询)

其实你可以用KVO或者通知中心来替代定时器轮询,让AppDelegate的计数变量变化时主动通知TableViewController更新,这样更高效也更优雅:

用KVO实现的示例(Objective-C)

  1. 先在AppDelegate里把计数变量声明为可观察属性:
@property (atomic, assign) NSInteger countVariable;
  1. 在TableViewController里监听这个变量的变化:
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate addObserver:self forKeyPath:@"countVariable" options:NSKeyValueObservingOptionNew context:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [super viewWillDisappear:animated];
    AppDelegate *appDelegate = (AppDelegate *)[UIApplication sharedApplication].delegate;
    [appDelegate removeObserver:self forKeyPath:@"countVariable"];
}

// KVO回调,实时刷新可见单元格
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context {
    if ([keyPath isEqualToString:@"countVariable"]) {
        [self.tableView reloadRowsAtIndexPaths:self.tableView.indexPathsForVisibleRows withRowAnimation:UITableViewRowAnimationNone];
    }
}

这样只要AppDelegate里的计数变量一变化,TableViewController就会立刻收到通知并刷新UI,完全不需要定时器。


注意事项

  • 不管用哪种定时器方案,一定要在控制器销毁时调用invalidate方法,避免内存泄漏;
  • 用KVO时必须在视图消失或控制器销毁时移除监听,否则会引发崩溃。

内容的提问来源于stack exchange,提问作者Riccardo Perego

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 08:06:53