Xcode 9.3(Swift4.1)中Observing property导致Instruments(Leaks profile)崩溃
KVO在Instruments Leaks模式下崩溃的问题分析与修复
我来帮你拆解这个奇怪的崩溃场景,并给出针对性的解决方案:
问题重现场景
你遇到的情况很典型:
- 正常Run模式运行代码时,两条
os_log日志都能正常打印,程序毫无问题 - 但通过Memory Debug Navigator点击Restart启动Instruments的Leaks分析时,程序直接崩溃在
observe(...)方法处,只有第一条初始化日志能打印出来
补全后的代码片段(方便理解上下文):
import os import UIKit class ObserverCrashingExample: NSObject { @objc private var animation: UIViewPropertyAnimator? private var observer: NSKeyValueObservation? override init() { super.init() os_log("Initializing ObserverCrashingExample") animation = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { // 空动画闭包 } // 崩溃触发点 observer = animation?.observe(\.isRunning, options: [.initial, .new]) { [weak self] _, change in guard let self = self else { return } os_log("Observed isRunning change: %@", String(describing: change.newValue)) } } }
崩溃原因深挖
这种特定场景下的崩溃,核心原因是Instruments的Leaks检测模式会修改系统内存管理的行为:
- 当通过Memory Debug Navigator启动分析时,系统会启用额外的内存检测钩子,可能提前对对象进行引用计数的检查或释放操作
UIViewPropertyAnimator本身是一个状态敏感的对象,在内存检测模式下,它的内部初始化流程可能被打断,导致在你调用observe时,对象还未完全稳定- 另外,虽然你用了
[weak self]避免循环引用,但如果animation被提前释放,observe返回的NSKeyValueObservation会绑定到一个无效对象上,触发野指针崩溃
可行的修复方案
试试下面几种方法,应该能解决这个问题:
1. 延迟执行KVO观察
把KVO的代码放到animation完全初始化后的下一个主循环周期,确保对象状态稳定:
override init() { super.init() os_log("Initializing ObserverCrashingExample") animation = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { // 空动画闭包 } // 延迟到下一个主循环执行观察 DispatchQueue.main.async { [weak self] in guard let self = self, let animation = self.animation else { return } self.observer = animation.observe(\.isRunning, options: [.initial, .new]) { _, change in os_log("Observed isRunning change: %@", String(describing: change.newValue)) } } }
2. 显式管理对象生命周期
在deinit中手动清理KVO观察和动画对象,确保内存释放的顺序正确:
deinit { // 先取消KVO观察 observer?.invalidate() // 停止动画并释放 animation?.stopAnimation(true) animation = nil os_log("Deinitializing ObserverCrashingExample") }
3. 替换KVO为回调闭包
如果业务场景允许,尽量用UIViewPropertyAnimator自带的闭包回调替代KVO,避开KVO在内存检测模式下的兼容性问题:
animation = UIViewPropertyAnimator(duration: 1.0, curve: .linear) { // 空动画闭包 } // 监听动画完成状态 animation?.addCompletion { position in os_log("Animation completed with position: %@", String(describing: position)) } // 如果需要实时监听运行状态,可以结合定时器或者其他状态回调方式
内容的提问来源于stack exchange,提问作者Mark Jarecki
相关产品推荐
相关产品推荐

