Swift 4网络异常崩溃排查:主线程调用问题及错误处理咨询
嘿,我来帮你拆解下这个问题,先从崩溃根源说起,再逐一解答你的两个疑问:
首先,你遇到的NSInternalInconsistencyException崩溃,核心原因是所有UI相关操作必须在主线程执行。URLSession的dataTask回调是在后台线程跑的,而你的代码里有几处调用completion的地方没放到主线程——比如错误分支、JSON解析失败分支、switch的default分支,如果这些completion闭包内部做了UI操作(比如弹提示框、更新界面),就会触发系统的主线程检查,包括你看到的UIKeyboardTaskQueue相关报错,本质也是后台线程触发了UI相关任务导致的。
a) 哪些位置需要用DispatchQueue.main.async?
记住一个核心规则:任何会触发UI更新、或者调用UIKit API的代码,必须放在主线程。具体到你的代码里,所有调用completion的地方都要包在DispatchQueue.main.async里——因为你没法保证调用方在completion里不会做UI操作:
- 最容易忽略的:错误分支(
if let error = error)里的completion(self.errorMessage, nil) - switch的default分支里的
completion("Wrong request call",nil) - JSON解析失败分支(
catch let jsError)里的completion("Error Serialising JSON",nil) - 你已经处理的三个case里的completion是对的,继续保持
- 另外,状态码非200的分支(比如401-500、500)如果后续要通过completion通知上层,也必须放到主线程
举个修改后的错误分支示例:
if let error = error { self.errorMessage += "Data Gathering Error: " + error.localizedDescription + "\n" // 把completion放到主线程 DispatchQueue.main.async { completion(self.errorMessage, nil) } return }
b) 错误处理的合理性与1009错误针对性处理
直接用error.localizedDescription处理错误并不够精准,它只能给你系统本地化的通用提示,没法针对性处理特定错误类型。
要精准捕获网络断开的1009错误(对应系统常量NSURLErrorNotConnectedToInternet),你需要把错误转换成NSError,然后判断它的code:
if let nsError = error as NSError? { switch nsError.code { case NSURLErrorNotConnectedToInternet: // 对应1009错误码 self.errorMessage += "网络连接已断开,请检查你的网络设置\n" case NSURLErrorTimedOut: self.errorMessage += "请求超时,请稍后重试\n" // 可以根据需求添加更多需要特殊处理的错误码 default: self.errorMessage += "Data Gathering Error: " + nsError.localizedDescription + "\n" } DispatchQueue.main.async { completion(self.errorMessage, nil) } return }
这样就能给用户更明确的错误提示,同时也能覆盖其他未知错误的处理。
最后提个小建议:你代码里的状态码判断(比如401、500)目前只是print日志,建议也通过completion通知上层,并且同样要放在主线程执行哦。
内容的提问来源于stack exchange,提问作者user165242

