Swift 5:我无法及时更新我的 UITableView (同时使用DispatchQueue.global().sync
和DispatchQueue.main.async
简而言之:
- 我有一个简单的
for index in 1…20
循环,可以做一些"东西"。 - 在 20 次迭代中的每次迭代结束时,我都会将一个新元素追加到作为 UITableView 数据源的数组中。
- 我只是希望 UITableView 在开始循环的下一次迭代之前更新显示。
- 真的有那么多要问的吗?大声笑
我尝试将所有"工作"放在后台线程上,我什至创建了一个带有QoSClass.utility
线程,无论我做什么......它完全忽略所有出现的DispatchQueue.main.async
,直到整个函数完成。
我可以毫无问题地完成我的代码需要完成的工作。 在这一点上,我的挑战是控制事情发生的顺序并让显示器更新(并保持对用户的响应,目前没有这样做)。
编辑/更新:
使用下面的答案...如果我使用.async
我会得到我想要的及时更新,但我没有得到顺序 1、2、3 的索引结果......20. 如果我使用.sync
我会得到我想要的订单,但没有更新。如何让我的循环按顺序执行并获取更新以在每个循环显示一次?
我对这里的线程非常陌生,所以请慢慢输入。 谢谢!
这是我非常精简的代码:
//
// ValidateViewController.swift
// MySpecialProject
//
// Created by Jim Termini on 10/16/21.
//
import UIKit
import PDFKit
import UniformTypeIdentifiers
import CoreData
class ValidateViewController: UIViewController, PDFViewDelegate {
@IBOutlet weak var tableView: UITableView!
var statuses: [ValidationStatus] = []
//
// START HERE: ViewDidLoad
//
override func viewDidLoad() {
super.viewDidLoad()
statuses = createStatusArray()
tableView.delegate = self
tableView.dataSource = self
}
//
// Button Pressed — Start Validating the Document!!
//
@IBAction func startValidatIn(_ sender: Any) {
print(" ")
print(" ")
theMainThing()
print(" ")
print(" ")
}
//
// theMainThing function called when button pushed.
//
func theMainThing() {
print(" ")
print(pickedDocSourceFPnFURL ?? "No File Picked")
let openResults = openFile(pdfURLToSearch: pickedDocSourceFPnFURL!)
originalPDFDocument = openResults.0
metaPDFModifiedDate = openResults.1
var unaccountedPageCount = orginalDocumentPageCount
// Keep track of the total number of Forms found in the Document
var theFormCount = 0
let JimmyQoS: DispatchQoS.QoSClass = DispatchQoS.QoSClass.utility
let JimmysQue: DispatchQueue = DispatchQueue.global(qos: JimmyQoS)
var countOfFormsOfThisPageLength: Int = 0
var dummyVar: Int = 0
for index in 1...20 {
JimmysQue.sync {
print(" ")
if index == 6 {
//
// Do special case stuff when index = 6
//
} else if index == 1 {
//
// Do special case stuff when index = 6
//
} else {
//
// Do regular case stuff when index = 2 – 5, 7 – 20.
//
}
print(" ")
print("The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!")
DispatchQueue.main.async {
print(" (Date()) ")
print("Here I am ... INSIDE the main thread async.")
if countOfFormsOfThisPageLength > 0 {
let term = (countOfFormsOfThisPageLength > 1) ? "forms" : "form"
self.statuses.append((ValidationStatus(image: StatusIcon.allGood, title: "Identified (countOfFormsOfThisPageLength) (term) of (index) pages each.", details: "Unaccounted for Pages: (dummyVar) of (self.orginalDocumentPageCount)")))
self.tableView.reloadData()
self.tableView.setNeedsDisplay()
}
print("This SHOULD be causing my display to update.")
print(" (Date()) ")
}
print(" (Date()) ")
print("Here I am ... about to sleep the global (background) thread for 5 seconds to ensure the TableView is updated and displayed properly.")
sleep(5)
print("And here I am ... 5 seconds later ... still no UIViewTable Update :-(")
print(" (Date()) ")
}
}
}
}
这是应该发生的事情和应该发生的事情的演练......
- 我按下"开始验证"按钮
- 两行紫色/绿色印花 ✅
- 调用
theMainThing()
函数 ✅ - 一行红色/黄色打印 ✅
for index in 1…20
循环开始 ✅- 黄线在每个循环✅的开头打印
- 我的处理确实做了它应该做的事情 ✅
- 在
DispatchQueue.main.async
块应该以更高的优先级执行更新UITableView
之前,会打印一条红线。 ✅ - 主 (UI) 线程应该启动并 ❌
- 打印绿色行 ❌
- 更新 UITableView/Display ❌
- 打印第二个绿色行 ❌
- 要给 UI 时间更新...我
- 打印紫色行 ✅
- 使后台队列休眠 5 秒钟 ✅
- 打印另一个紫色行 ✅
这是我的实际控制台输出:
来自高优先级主线程的绿线直到for index in 1…20
循环完成后 +theMainThing()
函数完成后 +startValidatIn(_ sender: Any)
函数完成后才会执行! !️
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
2021-10-17 02:34:21 +0000
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
2021-10-17 02:34:26 +0000
-------------------------
18 Duplicate Sets Omitted
-------------------------
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
2021-10-17 02:34:26 +0000
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
2021-10-17 02:34:31 +0000
2021-10-17 02:46:34 +0000
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
2021-10-17 02:46:34 +0000
2021-10-17 02:46:34 +0000
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
2021-10-17 02:46:34 +0000
这是我的控制台输出应该是:
每条红线之后应该是高优先级主 UI 线程的绿线。
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
2021-10-17 02:46:34 +0000
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
2021-10-17 02:46:34 +0000
2021-10-17 02:34:21 +0000
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
2021-10-17 02:34:26 +0000
The NEXT Line is "DispatchQueue.main.async" SHOULD see GREEN row NEXT!!
2021-10-17 02:46:34 +0000
Here I am ... INSIDE the main thread async.
This SHOULD be causing my display to update.
2021-10-17 02:46:34 +0000
2021-10-17 02:34:26 +0000
Here I am ... about to sleep the global (background) thread for 5 seconds
to ensure the TableView is updated and displayed properly.
And here I am ... 5 seconds later ... still no UIViewTable Update :-(
2021-10-17 02:34:31 +0000
当用户单击按钮时,您位于主线程上;它正在处理该按钮单击事件。
在处理该事件时,然后创建一个队列JimmysQue
并使用JimmysQue.sync { ... }
对其执行同步操作。
通过调用sync
您是在说"我想在另一个队列上执行此操作,但我也想(同步)等到另一个队列完成。 换句话说,您已将主线程置于暂停状态,直到您调度到JimmysQue
的块完成。
运行该后台操作时,JimmysQue
调用DispatchQueue.main.async
。 您可能与此关联的概念模型是"将事件发送到主事件事件循环,该循环将在没有处理其他事件时运行此代码块。
但请记住,主线程仍在处理您的点击事件! 您暂停了它,直到JimmysQue
可以完成运行其块。 因此,在按钮处理程序完成并且按钮处理程序仍在等待发送到JimmysQue
的其余活动之前,已调度到主线程的内容不会运行。
我认为可以使您获得所追求的效果的简单更改是使用async
而不是sync
将块发送到JimmysQue
。
验证块将被发送出去在后台运行。主线程不会等待它(按钮单击事件将完成,主线程将关闭以处理任何其他事件)。
然后,当JimmysQue
上的块完成验证时,它将使用DispatchQueue.main.async
发送块,以便在它有一些空闲时间时在主线程上执行。
我... 打字...这。。。非常。。。慢慢。。。
下面是一个更精简的示例,用于演示常规流。 你可以把它粘贴到游乐场中。尝试将.utility
队列上的呼叫从async
更改为sync
,以查看行为如何变化。
import Foundation
func theMainThing() {
print("Dispatching blocks from main thread")
for index in 1...20 {
DispatchQueue.global(qos: .utility).async {
// Sleep for a random amount of time to simulate a long
// running Action
sleep((1...5).randomElement()!)
DispatchQueue.main.async {
print("The main thing's index number (index) completed")
}
}
}
print("The Main Thread has gone on to bigger and better things")
}
theMainThing()
在评论中,您询问了有关订购完成结果的问题。 下面是一个示例:
import Foundation
func theMainThing() {
print("Dispatching Async blocks from main thread")
let scottsQueue = DispatchQueue(label: "ValidationQueue", qos: .utility)
for index in 1...20 {
scottsQueue.async {
print("The starting item (index)")
// Sleep for a random amount of time to simulate a long
// running Action
sleep((1...5).randomElement()!)
DispatchQueue.main.sync {
print("The main thing's index number (index) completed")
}
}
}
print("The Main Thread has gone on to bigger and better things")
}
theMainThing()
请注意,我们将所有验证块放入串行队列中,以便严格按顺序执行它们。 每个块都使用sync
调度到主队列,因此每个后台任务在完成之前等待主队列确认完成。