Swift 3下载数据时界面冻结问题求解(线程相关)
解决Swift 3聊天列表加载时界面冻结的问题
首先,界面冻结的核心原因是主线程被耗时的网络请求、数据处理操作阻塞了。iOS要求所有UI操作必须在主线程执行,但耗时的逻辑(比如数据解析、循环处理、同步网络请求)必须放到后台线程,处理完成后再切回主线程更新UI。
针对你的代码,我会分步骤说明修改点:
1. 把耗时的数据处理逻辑移到后台线程
在getChatList的SocketIO回调中,你对messages的遍历、字典解析、调用getOponentsDataForChat这些操作都是耗时的,应该放到后台队列执行,避免阻塞主线程。
修改后的getChatList大致如下:
func getChatList() { SocketIOManager.sharedInstance.getChatList { [weak self] (messages) in guard let s = self else { return } s.timer?.invalidate() // 把耗时的数据处理移到后台队列(选择用户发起级别的QoS,优先级适配聊天场景) DispatchQueue.global(qos: .userInitiated).async { if messages.isEmpty { // 空列表的UI更新必须切回主线程 DispatchQueue.main.async { s.activityIndicator.stopAnimating() s.tableView.emptyDataSetSource = s s.tableView.emptyDataSetDelegate = s s.tableView.reloadData() } } else { // 遍历处理messages,这部分耗时操作放在后台执行 messages.forEach({ (dict) in guard dict["last_message"].string != nil else { return } if dict["target"].string != "group" { if let messageID = dict["_id"].string { let str = messageID.components(separatedBy: "_") let number1 = str[0] let number2 = str[1] if number1 != number2 || number1 != Defaults[.msisdn] { let number = s.returnOpponentData(messageID: messageID) let message = LastMessage(dictionary: dict) // 若getOponentsDataForChat是异步请求,回调内更新UI必须切主线程 ChatNetworkManager.shared.getOponentsDataForChat(number: number, lastMessage: message, handler: { [weak s] result in DispatchQueue.main.async { // 比如更新chats数组、刷新表格这类UI相关操作都放这里 // s?.chats.append(...) // s?.tableView.reloadData() } }) } } } else { // 群聊逻辑同样放在后台处理,完成后切主线程更新UI // ... } }) // 所有数据处理完成后,停止加载动画并刷新UI DispatchQueue.main.async { s.activityIndicator.stopAnimating() s.tableView.reloadData() } } } } }
2. 确保所有UI操作都在主线程执行
你已经在空列表处理时用了DispatchQueue.main.async,但还要注意:
- 任何更新UI的操作(比如
activityIndicator.stopAnimating()、tableView.reloadData()、设置数据源代理)都必须放在主线程。 getOponentsDataForChat的回调中,只要涉及更新数据源或UI,都要切回主线程(上面代码中已标注)。
3. 检查viewDidLoad中的线程逻辑
在viewDidLoad里,activityIndicator.startAnimating()是主线程操作没问题,但SocketIO的statusChange回调如果涉及UI更新或后续耗时操作,也要切回主线程:
socket.on(clientEvent: .statusChange) { [unowned self] data, ack in DispatchQueue.main.async { switch self.socket.status { // 处理状态变更的UI逻辑 // ... } self.setupChat() } }
关键注意点
- 绝对不要用
DispatchQueue.main.sync!sync会阻塞当前线程等待主线程完成,一旦当前线程是主线程就会触发死锁,你需要的是async,让主线程异步执行UI操作,不阻塞后台处理流程。 - 后台队列的QoS选择:
.userInitiated适合用户主动触发的操作(比如加载聊天列表),优先级较高,能保证处理速度。 - 如果
ChatNetworkManager.shared.getOponentsDataForChat是异步网络请求,它本身已经在后台执行,但回调大概率不在主线程,所以更新UI时必须手动切回主线程。
这样修改后,耗时的数据处理都在后台线程执行,主线程只负责UI更新,就不会出现界面冻结的问题了。
内容的提问来源于stack exchange,提问作者Muslim Ismailov
相关产品推荐
相关产品推荐

