Alamofire会话失效问题求助:单例网络请求重复调用崩溃
单例模式下Alamofire Session失效问题求助
我在网络请求中使用单例模式,却遇到了Session失效的问题,错误提示如下:
Session was invalidated without error, so it was likely deinitialized unexpectedly. Be sure to retain a reference to your Session for the duration of your requests.
我的网络封装代码:
public static let shared = NetworkingClient() private var requests = [ApiRequest]() private var completedRequests = 0 private var session: Session! func InvalidateSession () { if #available(iOS 9.0, *) { Alamofire.Session.default.session.getAllTasks { (tasks) in tasks.forEach{ $0.cancel() } } } else { Alamofire.Session.default.session.getTasksWithCompletionHandler { (sessionDataTask, uploadData, downloadData) in sessionDataTask.forEach { $0.cancel() } uploadData.forEach { $0.cancel() } downloadData.forEach { $0.cancel() } } } }
API调用代码(场景:增加quantity时Alamofire崩溃,希望避免重复请求,仅调用一次API):
func gasEstimation(chain_Id: Int, contractAddress: String, fromAddress: String, toAddress: String, tokenId: String, contractType: String, quantity: String) { let apiRequest = [ApiRequest(type: .get, path: "gasEstimation?chainId=\(chain_Id)&contractAddress=\(contractAddress)&from=\(fromAddress.trimmingCharacters(in: .whitespacesAndNewlines))&to=\(toAddress.trimmingCharacters(in: .whitespacesAndNewlines))&type=\(contractType)&quantity=\(quantity)&tokenId=\(tokenId)")] NetworkingClient.shared.performRequest(requests: apiRequest) { path, data, error in if data != nil { let json = JSON(data!) let gasTokenModel = NftTransferModel(json: json) self.presenter?.onReceiveFee(estimationFee: gasTokenModel,error: nil) }else{ let error = JSON(error!) self.presenter?.onReceiveFee(estimationFee: nil,error: error["message"].stringValue) } } }
问题分析
错误提示明确指出核心问题:Session被意外释放,必须在请求全程持有Session的引用。你的代码存在以下几个关键问题:
- 单例
NetworkingClient中声明了session属性但从未初始化,反而直接依赖Alamofire.Session.default全局Session,其生命周期不受单例控制,极易被系统意外回收。 InvalidateSession方法操作的是全局默认Session,而非单例自身应持有的Session,导致请求管理逻辑混乱。- 未实现请求去重机制,quantity变化时会重复发起相同请求,加剧Session资源冲突,最终引发崩溃。
修复方案
1. 初始化并持有单例专属Session
修改NetworkingClient,创建并持有专属Session,避免依赖全局默认Session:
public static let shared = NetworkingClient() private var requests = [ApiRequest]() private var completedRequests = 0 // 初始化单例专属Session,确保全程强引用 private let session: Session = { let config = URLSessionConfiguration.default config.timeoutIntervalForRequest = 30 // 按需配置超时时间 return Session(configuration: config) }() // 方法名改为小驼峰,符合Swift命名规范 func invalidateSession() { // 操作单例自身的Session,而非全局默认Session session.session.getAllTasks { tasks in tasks.forEach { $0.cancel() } } }
2. 实现请求去重逻辑
在NetworkingClient中新增正在进行的请求管理,避免重复发起相同请求:
// 存储正在进行的请求,用请求唯一标识作为key private var ongoingRequests = [String: DataRequest]() func performRequest(requests: [ApiRequest], completion: @escaping (String, Data?, Error?) -> Void) { for request in requests { // 生成请求唯一标识(可根据path+参数组合生成) let requestKey = request.path // 检查是否已有相同请求在进行,存在则取消旧请求 if let existingRequest = ongoingRequests[requestKey] { existingRequest.cancel() ongoingRequests.removeValue(forKey: requestKey) } // 发起新请求并加入管理字典 let dataRequest = session.request(request.path, method: request.type) .responseData { [weak self] response in self?.ongoingRequests.removeValue(forKey: requestKey) switch response.result { case .success(let data): completion(request.path, data, nil) case .failure(let error): completion(request.path, nil, error) } } ongoingRequests[requestKey] = dataRequest } } // 供外部检查请求是否正在进行的方法 func isRequestOngoing(key: String) -> Bool { return ongoingRequests[key] != nil }
3. 优化API调用的参数处理
避免直接拼接URL参数,改用Alamofire参数传递方式,同时生成可靠的请求标识:
func gasEstimation(chain_Id: Int, contractAddress: String, fromAddress: String, toAddress: String, tokenId: String, contractType: String, quantity: String) { let parameters: [String: Any] = [ "chainId": chain_Id, "contractAddress": contractAddress, "from": fromAddress.trimmingCharacters(in: .whitespacesAndNewlines), "to": toAddress.trimmingCharacters(in: .whitespacesAndNewlines), "type": contractType, "quantity": quantity, "tokenId": tokenId ] // 生成唯一请求标识:排序参数后拼接,确保相同参数组合生成相同key let sortedKeys = parameters.keys.sorted() var requestKey = "gasEstimation?" for key in sortedKeys { if let value = parameters[key] as? String { requestKey += "\(key)=\(value)&" } else if let value = parameters[key] as? Int { requestKey += "\(key)=\(value)&" } } requestKey = String(requestKey.dropLast()) // 检查请求是否正在进行,避免重复调用 if NetworkingClient.shared.isRequestOngoing(key: requestKey) { return } // 注意:需要修改ApiRequest支持参数传递,或者直接用Alamofire的request方法传入parameters let apiRequest = ApiRequest(type: .get, path: "gasEstimation", parameters: parameters) NetworkingClient.shared.performRequest(requests: [apiRequest]) { path, data, error in if let data = data { let json = JSON(data) let gasTokenModel = NftTransferModel(json: json) self.presenter?.onReceiveFee(estimationFee: gasTokenModel, error: nil) } else if let error = error { let jsonError = JSON(error) self.presenter?.onReceiveFee(estimationFee: nil, error: jsonError["message"].stringValue) } } }
关键注意事项
- 必须持有Session强引用:确保Session的生命周期覆盖所有请求时长,避免被提前释放。
- 统一Session使用:所有请求都通过单例自身的Session发起,不要混合使用全局默认Session。
- 请求去重逻辑:通过唯一标识管理正在进行的请求,避免同一参数组合重复发起请求。
内容的提问来源于stack exchange,提问作者Muhammad Bilal Raza
相关产品推荐
相关产品推荐

