objc_getClassList返回部分类不可访问 遍历类列表偶发崩溃如何解决
遍历Objective-C全量类偶现
Attempt to use unknown class 崩溃修复方案 崩溃根因
这个问题和类列表缓冲区的分配逻辑没有关系,核心原因是Objective-C运行时的类表从来不是线程安全的静态快照:
- 系统、第三方SDK(包括你碰到的带UUID串的
fir_前缀Firebase动态类)会在后台线程注册、注销临时类,dyld加载/卸载Mach-O镜像时也会修改全局类表 - 不管是
objc_getClassList还是objc_copyClassList,都不会持有返回类的引用,也不会在遍历期间给类表加锁。拿到类指针之后,如果刚好对应类被注销、所在的镜像被卸载,这个指针就成了野指针,此时访问类名、调用class_conformsToProtocol等方法就会触发报错 - 性能差的设备遍历5万左右的类耗时更长,撞上类表变更的时间窗口概率更高,所以复现率更明显
- 换用
objc_copyClassList仍然崩溃是正常的:这个接口只是在调用的瞬间拷贝了当前的类指针数组,完全不保证后续遍历过程中这些指针始终合法 - 额外提一句:很多开发者误以为ObjC类一旦注册就永久存在,实际上从iOS 15开始系统支持动态库卸载,不少闭源SDK也会调用运行时接口注销临时生成的类,类指针不是永久有效的。
可落地修复方案
1. 访问类前加有效性校验+异常捕获
失效类被访问时会触发ObjC运行时的内部异常,加一层防护过滤掉无效指针即可彻底避免崩溃,参考逻辑:
// 注:ObjCException.catch是轻量的ObjC异常包装,自行实现即可:写个ObjC工具类,用@try/@catch包裹执行block,捕获到NSException时直接返回,无额外依赖 func validateClass(_ rawCls: AnyClass) -> AnyClass? { var validCls: AnyClass? = nil ObjCException.catch { guard let clsName = NSStringFromClass(rawCls) as? String else { return } // 能通过类名反查到相同指针的类,才判定为有效 if let lookedUpCls = NSClassFromString(clsName), lookedUpCls === rawCls { validCls = lookedUpCls } } return validCls }
遍历类列表时,先调用上述方法校验,拿到合法的类对象之后再执行后续逻辑,就能把所有失效的野指针类过滤掉。
2. 提前过滤已知的临时动态类
你碰到的崩溃点集中在Firebase生成的临时类,这类类本身是SDK运行时动态生成、用完即销毁的,完全不需要纳入遍历范围,可以直接通过类名前缀过滤,减少无效访问:
// 按需补充不需要处理的临时类前缀即可 let skipPrefixes = [ "fir_", "Aspects_", "_TtGC", // Swift动态生成的泛型临时类 "OS_", // 系统内部临时类 "__NS" ] let clsName = NSStringFromClass(rawCls) if skipPrefixes.contains(where: { clsName.hasPrefix($0) }) { continue }
3. 降低类指针失效概率
- 拿到类列表后立刻做遍历校验,不要在遍历循环里插入耗时操作
- 不要长期缓存类指针,如果需要持久化记录类信息,只存类名字符串即可,后续使用时再通过
NSClassFromString重新获取类,从根源上避免野指针问题 - 尽量选在App完全启动、所有SDK初始化完成的空闲时机执行类遍历逻辑,避开启动阶段大量SDK动态注册/注销类的高峰窗口,进一步降低撞错概率。
内容的提问来源于stack exchange,提问作者user1232690
相关产品推荐
相关产品推荐

