當用戶手指按住屏幕拖動 UITableView 時,主線程 RunLoop 從 kCFRunLoopDefaultMode 切換至 UITrackingRunLoopMode(系統內部模式,對應 NSRunLoopCommonModes 標記)。默認模式下添加的 NSTimer、CADisplayLink、GCD 主隊列任務 均會被暫停,導致彈幕動畫、倒計時等依賴主線程定時觸發的邏輯凍結。
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0
target:self
selector:@selector(updateDanmaku:)
userInfo:nil
repeats:YES];
// 關鍵:註冊到 Common Modes,包含 DefaultMode 與 TrackingMode
[[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
優點:一行代碼解決,無需修改業務邏輯。
適用場景:低頻定時器(>16ms)、非極高精度動畫。
CADisplayLink *link = [CADisplayLink displayLinkWithTarget:self selector:@selector(renderFrame:)];
[link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
// 關鍵:CADisplayLink 綁定屏幕刷新率,配合 Common Modes 實現滑動不掉幂
優點:與屏幕 VSync 同步,動畫流暢度最佳;自動適配 ProMotion 120Hz 屏幕。
注意:preferredFramesPerSecond 控制幂率,避免過度消耗 CPU。
// 後台線程
dispatch_queue_t renderQueue = dispatch_queue_create("com.app.danmaku.render", DISPATCH_QUEUE_SERIAL);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, renderQueue);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 16 * NSEC_PER_MSEC, 0);
dispatch_source_set_event_handler(timer, ^{
// 計算彈幕位置、碰撞檢測等純計算邏輯
DanmakuFrame *frame = [self computeNextFrame];
// 回主線程僅做 CALayer 屬性更新
dispatch_async(dispatch_get_main_queue(), ^{
[self updateLayersWithFrame:frame];
});
});
dispatch_resume(timer);
優點:完全脫離主線程 RunLoop 限制,滑動時計算不阻塞、渲染不掉幂。
適用場景:數百條彈幕並發、複雜碰撞檢測、粒子特效等重計算場景。
CATextLayer 或位圖緩存,滑動時僅修改 position/transform,避免重複排版。UITableViewCell 機制,實現彈幕 Label/Layer 的對象池,減少分配開銷。CADisplayLink 設置 preferredFramesPerSecond = 30 降低功耗;後台計算隊列設置 QOS_CLASS_UTILITY。NSRunLoopCommonModes 與 UITrackingRunLoopMode 雙重保障,應對系統版本差異。核心原則:將「計算」與「渲染」解耦,計算放後台或 Common Modes Timer,渲染綁定 CADisplayLink,實現滑動流暢與動畫連續的雙贏。