建筑师之家IOS / NOTES
← 全部笔记

性能

主线程只负责提交这一帧

卡顿先分清是单帧超时还是主线程被占住几秒。两种问题用的工具不一样。

60Hz 下一帧大约 16.7ms,ProMotion 下更短。主线程要在截止时间前完成事件、布局、绘制和提交。超时一帧,滚动会顿一下,这是 hitch。主线程连续几百毫秒不返回,界面像死了,这是 hang。先分清你在修哪一种。

什么不该在主线程

JSON 解码、图片解码、数据库查询、文件读写、压缩、正则扫一大段文本,都不该占着主线程。判断标准不是“这段代码短”,而是它的输入会不会变大。今天 20KB 的配置明天可能是一份完整目录。

常见的假异步:后台线程算完,主线程又把结果再处理一遍,比如把模型数组 map 成另一种模型、算高度、拼富文本。真正贵的那一步还在主线程上。

struct FeedItem: Sendable {
    var id: String
    var title: String
}

func loadFeed(from data: Data) async throws -> [FeedItem] {
    try await Task.detached(priority: .userInitiated) {
        let decoded = try JSONDecoder().decode([FeedItem].self, from: data)
        return decoded
    }.value
}

回到主线程只做赋值。UIImageView.image = 已经解码好的图,table 的 reload 或 snapshot apply,这些才是主线程的活。从主线程调用 DispatchQueue.main.sync 会直接死锁,这种代码不要留在重试逻辑里。

怎么看

QoS 也要配对。用户正在等的跳转用 userInitiated。统计、清理用 utility。把重活丢到 default 再在里面抢主线程,队列会反过来堵。

修的时候给嫌疑区域打 os_signpost,区间要包住完整工作,而不是只包一行赋值。下一次录制能直接看到这段占了多少毫秒,比在 Time Profiler 里猜方法名快。