获取带电压数据的单条iOS ECG测量值致Flutter应用崩溃
解决Flutter iOS ECG电压数据获取崩溃问题
核心崩溃原因分析
EXC_BAD_ACCESS错误指向空指针访问,结合代码排查,问题根源在于:
HKElectrocardiogramQuery对象执行后被ARC自动释放,异步回调时访问已回收的内存- 多线程环境下对
ecgs、measurments等变量的修改存在线程安全隐患 - 批量查询时变量生命周期与异步回调不匹配,导致内存异常
具体解决方法
1. 强引用查询对象,避免提前释放
在循环创建voltageQuery后,将其存入一个闭包内的数组中,直到查询完成再移除,防止ARC提前回收对象。
2. 保证线程安全
使用串行队列或锁保护对ecgs和measurments的修改操作,避免多线程竞争引发的内存问题。
3. 优化内存与初始化逻辑
- 若ECG样本数量较多,可分批发起查询,降低内存占用
- 检查
Electrocardiogram和VoltageMeasurement的初始化代码,避免强制解包空值
修改后的Swift代码示例
let query = HKSampleQuery( sampleType: type, predicate: predicate, limit: limit, sortDescriptors: sortDescriptors ) { (query, data, error) in guard error == nil, let results = data as? [HKElectrocardiogram] else { resultsHandler([], error) return } var ecgs: [Electrocardiogram] = [] var ecgsError: Error? let group = DispatchGroup() let serialQueue = DispatchQueue(label: "com.ecg.query.queue") // 强引用查询对象,防止提前释放 var activeQueries: [HKElectrocardiogramQuery] = [] switch withVoltageMeasurements { case true: for ecgSample in results { group.enter() var measurements = [Electrocardiogram.VoltageMeasurement]() let voltageQuery = HKElectrocardiogramQuery(ecgSample) { (query, result) in serialQueue.async { switch(result) { case .measurement(let voltageMeasurement): if let measurement = try? Electrocardiogram.VoltageMeasurement(voltageMeasurement: voltageMeasurement) { measurements.append(measurement) } case .done: if let ecg = try? Electrocardiogram(electrocardiogram: ecgSample, voltageMeasurements: measurements) { ecgs.append(ecg) } // 移除已完成的查询 if let index = activeQueries.firstIndex(where: { $0 === query }) { activeQueries.remove(at: index) } group.leave() case .error(let error): ecgsError = error if let index = activeQueries.firstIndex(where: { $0 === query }) { activeQueries.remove(at: index) } group.leave() @unknown default: ecgsError = HealthKitError.notAvailable("Unknown case of Electrocardiogram.VoltageMeasurement result") if let index = activeQueries.firstIndex(where: { $0 === query }) { activeQueries.remove(at: index) } group.leave() } } } // 强引用查询对象 activeQueries.append(voltageQuery) healthStore.execute(voltageQuery) } group.notify(queue: .global()) { resultsHandler(ecgs, ecgsError) } case false: ecgs = Electrocardiogram.collect(results: results) resultsHandler(ecgs, nil) } }
额外建议
- 优先测试单条ECG数据的获取,逐步验证稳定性后再批量查询
- 确认HealthKit权限申请完整,包含ECG相关的读写权限
- 排查
Electrocardiogram.VoltageMeasurement初始化方法,确保所有可选值都做了安全处理
内容的提问来源于stack exchange,提问作者hardanger
相关产品推荐
相关产品推荐

