Alamofire SessionManager随机触发EXC_BAD_ACCESS崩溃求解决方案
最近在Swift项目中使用Alamofire时,碰到了一个非常棘手的问题:随机出现不可复现的EXC_BAD_ACCESS崩溃,而且崩溃栈指向Alamofire的内部实现文件。经过反复调试和日志分析,发现问题大概率和网络请求的生命周期以及SessionManager的管理方式有关。下面把我的排查思路和解决方案分享出来,希望能帮到遇到同样问题的开发者。
先看代码里的潜在风险
从你提供的代码来看,核心问题在于每次请求都创建一个新的SessionManager实例,并把它存在managerStore字典中。这种做法其实违背了Alamofire的设计初衷,而且很容易引发内存管理问题:
// 每次请求都创建新的SessionManager let manager = createSessionManager() managerStore[managerId] = manager
可能的崩溃原因
- SessionManager生命周期不匹配:SessionManager内部持有
URLSession,而URLSession的delegate回调是异步的。如果请求还在进行中,对应的SessionManager就被从managerStore中移除或者被系统释放,当URLSession回调触发时,就会访问已经被销毁的对象,直接导致EXC_BAD_ACCESS。 - 线程安全问题:
managerStore是普通的Swift字典,而网络请求是在后台线程完成的,多线程环境下对字典进行读写操作,很容易导致字典内部结构损坏,进而引发崩溃。 - 旧版本Alamofire的已知bug:如果使用的Alamofire版本比较旧,可能存在SessionManager delegate处理的内存泄漏或释放时机问题,这些bug在新版本中已经被修复。
具体解决方案
1. 复用SessionManager(优先推荐)
Alamofire的SessionManager设计就是为了复用的,全局创建一个单例SessionManager,所有请求共用它,彻底避免频繁创建销毁带来的生命周期问题:
// 全局复用的SessionManager单例 let sharedSessionManager: SessionManager = { let config = URLSessionConfiguration.default // 配置默认请求头、超时时间等 config.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders config.timeoutIntervalForRequest = 30 return SessionManager(configuration: config) }()
之后在请求中直接使用这个单例,不再每次创建新的SessionManager。
2. 自定义请求头不用新建SessionManager
如果需要给不同请求设置自定义Header,完全不需要创建新的SessionManager,直接在请求层面修改Header即可:
open func makeRequest(method: HTTPMethod, encoding: ParameterEncoding, headers: [String:String]) -> DataRequest { // 合并默认Header和自定义Header var requestHeaders = SessionManager.defaultHTTPHeaders headers.forEach { requestHeaders[$0.key] = $0.value } return sharedSessionManager.request( URLString, method: method, parameters: parameters, encoding: encoding, headers: requestHeaders ) }
3. 修复managerStore的线程安全(如果必须保留多SessionManager)
如果你的业务场景确实需要临时存储SessionManager,一定要保证managerStore的线程安全:
// 创建专门的串行队列保护字典读写 private let managerStoreQueue = DispatchQueue(label: "com.yourapp.managerStore") private var managerStore: [String: SessionManager] = [:] // 存储SessionManager时 managerStoreQueue.sync { managerStore[managerId] = manager } // 移除SessionManager时(请求完成后立即清理) managerStoreQueue.sync { managerStore.removeValue(forKey: managerId) }
4. 请求完成后及时清理SessionManager
在processRequest的回调完成后,一定要从managerStore中移除对应的SessionManager,避免悬空引用:
func processRequest(request: Request, _ managerId: String, _ completion: @escaping (Response<T>?, Error?) -> Void) { request.response { [weak self] response in // 处理响应逻辑... // 及时清理对应的SessionManager self?.managerStoreQueue.sync { self?.managerStore.removeValue(forKey: managerId) } completion(...) } }
5. 升级Alamofire到最新稳定版
检查当前使用的Alamofire版本,尽量升级到最新的稳定版本,很多旧版本中SessionManager的内存管理bug已经被官方修复。
调试建议
- 开启Zombie Objects检测:在Xcode的Scheme设置中,勾选
Enable Zombie Objects,这样当访问已释放对象时,会给出更明确的错误提示,帮助定位具体是哪个对象被提前销毁。 - 使用Instruments排查内存问题:用Instruments的
Leaks工具检测是否存在循环引用,或者SessionManager的delegate是否有内存泄漏情况。
内容的提问来源于stack exchange,提问作者Puspendu Matilal

