Swift 5:我无法让我的 UITableView 及时更新(同时使用 `DispatchQueue.global().sync` 和 `DispatchQueue.main.async`

Posted

技术标签:

【中文标题】Swift 5:我无法让我的 UITableView 及时更新(同时使用 `DispatchQueue.global().sync` 和 `DispatchQueue.main.async`【英文标题】:Swift 5: I Am Unable to Get My UITableView To Update In A Timely Manor (Using both `DispatchQueue.global().sync` and `DispatchQueue.main.async` 【发布时间】:2021-12-04 15:19:10 【问题描述】:

Swift 5:我无法及时更新我的​​ UITableView(同时使用 DispatchQueue.global().syncDispatchQueue.main.async

简而言之:

我有一个简单的 for index in 1…20 循环可以做一些“事情”。 在 20 次迭代中的每一次结束时,我都会将一个新元素附加到作为我的 UITableView 的数据源的数组中。 我只是想让 UITableView 在开始下一次循环迭代之前更新显示。 真的有这么多要问的吗?哈哈

我试过把所有的“工作”放在后台线程上,我什至用QoSClass.utility创建了一个线程,不管我做什么......它完全忽略了所有出现的DispatchQueue.main.async,直到整个函数完成.

我可以毫无问题地完成我的代码需要完成的工作。在这一点上,我面临的挑战是控制事情发生的顺序并让显示更新(并保持对用户的响应,目前不会这样做)。

编辑/更新:

使用下面的答案......如果我使用 .async 我会得到我想要的及时更新但我没有得到按顺序 1、2 的索引结果, 3 ... 20. 如果我使用 .sync 我得到了我想要的订单但没有更新。 如何让我的循环按顺序执行并在每个循环中显示一次 GET UPDATES?

我对这里的线程非常陌生,所以请慢慢输入。谢谢!

这是我非常精简的代码:

//
//  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 秒 ✅ 再打印一个紫色行✅

这是我的 ACTUAL 控制台输出:

绿线,来自高优先级主线程,直到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????????????????????????????????????????????????????????????????????????????????????????????????????????????

????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????

【问题讨论】:

如果你真的不了解线程,并且如果你可以将你的应用程序限制在 ios 15 及更高版本,那么使用美妙的新 async/await 你会很多更快乐东西。 【参考方案1】:

当用户点击按钮时,你就在主线程上;它正在处理那个按钮点击事件。

在处理该事件时,然后创建一个队列 @9​​87654321@ 并使用 JimmysQue.sync ... 对其执行同步操作。

通过调用sync,你是在说“我想在另一个队列上执行这个操作,但我也想(同步地)等到另一个队列完成。换句话说,你已经把 Main线程暂停,直到您发送到JimmysQue 的块完成。

运行该后台操作时,JimmysQue 调用 DispatchQueue.main.async。您可能与此相关的概念模型是“向主事件事件循环发送一个事件,当没有其他事件正在处理时,它将运行此代码块。”

但请记住,主线程仍在处理您的点击事件!你暂停它直到JimmysQue 可以完成运行它的块。因此,在您的按钮处理程序完成并且您的按钮处理程序仍在等待您发送到JimmysQue 的其余活动之前,您已分派到主线程的东西不会运行。

我认为可以让您获得想要的效果的简单更改是使用async 而不是sync 将您的块发送到JimmysQue

验证块将被发送到后台运行。主线程不会等待它(按钮单击事件将完成,主线程将开始处理任何其他事件)。

然后,当JimmysQue 上的块完成验证时,它将使用DispatchQueue.main.async 发送块,以便在有空闲时间时在主线程上执行。

我...打字...这个...非常...慢...

这里有一个更精简的示例来演示一般流程。您可以将其粘贴到 Playground 中。尝试在.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()

您在 cmets 中询问有关订购完成结果的问题。这是一个例子:

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 分派到主队列,因此每个后台任务在完成之前等待主队列确认完成。

【讨论】:

谢谢‼️ 如果我使用 .async,我会及时获得我想要的更新,但是我没有按 1、2、3…20 的顺序获得索引结果。如果我使用 .sync我得到了我想要的订单,但没有更新。如何让我的循环按顺序执行并获取更新以在每个循环中显示一次?并且感谢您慢慢打字!哈哈 您要做的第一件事是创建一个串行队列,以便您发送到队列的块一个接一个地执行。您调度的第二个块在第一个块完成之前不会执行。然后,您将对Dispatch.main 的调用更改为Dispatch.main.sync。这样 background 线程每次都会暂停,直到主线程完成。 我添加了第二个代码示例来演示结果的排序。

以上是关于Swift 5:我无法让我的 UITableView 及时更新(同时使用 `DispatchQueue.global().sync` 和 `DispatchQueue.main.async`的主要内容,如果未能解决你的问题,请参考以下文章

让我的函数计算数组 Swift 的平均值

Swift:点击手势无法识别

iOS Swift:deleteRowsAtIndexPaths 崩溃 [重复]

无法让我在 Swift 中的计时器在 Playground 中触发

在我达到5之前,无法让我的脚本打印随机数

使用Swift 3和CoreData删除表视图行