Swift4中如何正确使用信号量同步Alamofire请求主线程?
正确使用信号量同步AlamofireXMLRPC请求的解决方案
我来帮你梳理下当前代码里的核心问题,然后给出适配Swift 4的正确实现:
你的代码存在的两个关键问题:
- 失败场景未唤醒信号量:
failureBlock里完全没调用semaphore.signal(),如果第一次请求就失败,信号量会一直处于等待状态,直接导致线程卡死甚至抛出异常。 - 主线程阻塞风险:直接在主线程调用
semaphore.wait()会彻底阻塞主线程,造成UI冻结,这在iOS开发中是绝对要避免的错误实践。
下面是修正后的完整代码,同时优化了线程调度和异常处理:
// 把整个同步逻辑移到后台队列执行,绝不阻塞主线程 DispatchQueue.global(qos: .background).async { let semaphore = DispatchSemaphore(value: 0) var count = 0 for i in idsCloud { guard !idsLocal.contains(i) else { continue } count += 1 print("\n---->O ID: \(i) NÃO CONSTA NO BANCO LOCAL! <----\n") OdooService.getProductByIdFromOdoo(id: i, successBlock: { (p) in // 保存产品后唤醒信号量 CoreDataHandler.saveProduct(p: p) semaphore.signal() }, failureBlock: { (erro) in // 请求失败也必须唤醒信号量,否则会永久等待 print(erro) semaphore.signal() }) // 等待当前请求完成,这里在后台队列,不会影响UI semaphore.wait() } // 同步完成后切回主线程更新UI或做后续处理 DispatchQueue.main.async { print("同步完成,共新增 \(count) 个产品") // 这里可以添加UI刷新逻辑,比如重新加载本地产品列表 } } // 修正后的OdooService方法,增加了数据解析的安全判断 static func getProductByIdFromOdoo(id: Int, successBlock: @escaping (_ params: [String:Any]) -> Void, failureBlock: @escaping (_ params: String) -> Void) { guard checkOdooConn() else { failureBlock("Falha na conexão com o Odoo") return } let options = ["fields": fieldsProducts] as [String:Any] let params = [OdooAuth.db, OdooAuth.uid, OdooAuth.password, "product.template", "search_read", [[["id","=",id]]], options] as [Any] print("start request...") AlamofireXMLRPC.request(OdooAuth.host2, methodName: "execute_kw", parameters: params) .responseXMLRPC(queue: DispatchQueue.global(qos: .background)) { (response: DataResponse<XMLRPCNode>) in switch response.result { case .success: // 安全解包响应数据,避免空值崩溃 guard let data = response.data, let str = String(data: data, encoding: .utf8) else { failureBlock("Falha ao analisar dados da resposta") return } let options = AEXMLOptions() guard let pdict = strToDict(str: str, options: options).first else { failureBlock("Falha ao converter dados para dicionário") return } successBlock(pdict) case .failure(let error): failureBlock(error.localizedDescription) } } }
核心优化说明:
- 后台队列执行同步逻辑:把循环和信号量操作放到后台队列,彻底避免主线程阻塞,保证APP的交互流畅性。
- 全场景信号量唤醒:无论请求成功还是失败,都调用
semaphore.signal(),彻底解决死锁问题,这也是你首次循环出现异常的核心原因。 - 安全的数据解析:在响应处理中添加了多层可选值解包判断,避免因服务器返回异常数据导致的崩溃。
- 规范的线程切换:同步完成后切回主线程处理UI更新,符合iOS开发的线程安全规范。
如果后续你想提升请求效率,也可以考虑用OperationQueue控制并发请求数(比如同时发起3个请求),但如果业务要求必须逐个串行请求,上面的方案就是最稳妥的实现。
内容的提问来源于stack exchange,提问作者Augusto
相关产品推荐
相关产品推荐

