Xcode中Swift多线程操作Class3遇EXC_BAD_ACCESS等错误求助
问题排查思路:Class3集合操作中的随机线程错误
背景概述
在Xcode Swift项目中:
Class3是仅包含变量的类,包含SOK、SO、SO2、SO3等集合类型变量Class2含@Published变量及后台计算函数,最终会在主线程将Class3的变量同步到自身@Published变量更新UIClass1作为调度类,通过串行OperationQueue和信号量调度Class2的127个操作
其中Class2的func_with_error函数(对应Class1的operation45)会随机触发两类错误:
Thread "X": EXC_BAD_ACCESS (code=1, address=0x18)(线程号不固定)Thread "X": "-[NSTaggedPointerString objectForKey:]: unrecognized selector sent to instance 0x8000000000000000"
错误集中在if class3.SOK[i] != nil或append(class3.SOK[i]!)代码行。
排查方向
1. 强制保障集合的线程安全性
Swift的Array、Dictionary等基础集合本身不是线程安全的,即便你认为操作是串行的,也可能存在未监控到的跨线程读写:
- 给
Class3的所有集合操作添加线程锁,用NSLock或os_unfair_lock包裹读写逻辑,示例:
替换class Class3 { private let lock = NSLock() var SOK: [Int: String] = [:] var SO: [String] = [] var SO2: [String] = [] var SO3: [String] = [] // 安全读取SOK值 func safeSOKValue(at index: Int) -> String? { lock.lock() defer { lock.unlock() } return SOK[index] } // 安全向SO添加元素 func safeAppendToSO(_ value: String) { lock.lock() defer { lock.unlock() } SO.append(value) } // 安全检查SO2是否包含指定值 func safeContainsInSO2(_ value: String) -> Bool { lock.lock() defer { lock.unlock() } return SO2.contains(value) } // 同理给SO3添加安全检查方法 }func_with_error中的直接集合访问为这些安全方法,验证错误是否消失。
2. 修复变量捕获的潜在风险
period变量在func_with_error外层定义,被内部BlockOperation闭包捕获,可能导致意外的内存访问:
- 将
period移到operation1闭包内部,避免跨闭包共享变量,并补充默认分支处理未匹配的time值:let operation1 = BlockOperation { let period: Int switch time { case "1min", "5min", "15min", "30min", "1h", "2h", "4h", "1day": period = 450 case "1month", "1week": period = 48 default: period = 0 // 避免未匹配时period为0导致异常循环 } // 后续循环逻辑使用此内部period变量 }
3. 验证SOK的类型与索引合法性
错误-[NSTaggedPointerString objectForKey:]说明存在类型混淆问题:
- 在
Class3中显式指定SOK的类型,比如var SOK: [Int: String] = [:],避免类型推断错误 - 检查所有修改
SOK的代码,确保没有将非字典类型的值赋值给它 - 确认循环中的
i值范围是否在SOK的键集合内,即便有nil判断,也要防范遍历过程中字典被修改导致的索引异常
4. 排查内存释放问题
class3在appeallingFunctions_fromClass2外层定义,被后台闭包捕获,可能提前释放导致野指针:
- 在
Class1中添加属性持有class3,确保其生命周期覆盖所有后台操作 - 开启Xcode的Zombie Objects工具,追踪已释放对象的访问,定位是否存在野指针问题
5. 简化代码定位问题
- 暂时注释
func_with_error中重复的判断逻辑(两次SO2.contains、两次SO3.contains),简化后验证错误是否还出现 - 将
OperationQueue替换为串行DispatchQueue的直接调用,排除BlockOperation依赖调度的潜在问题:DispatchQueue(label: "com.yourapp.serial").async { // 直接执行原operation1的逻辑 // 完成后调用semaphore.signal() }
内容的提问来源于stack exchange,提问作者Micsa Dan
相关产品推荐
相关产品推荐

