【问题标题】:Trouble understanding Queue's in Swift (async await)无法理解 Swift 中的队列(异步等待)
【发布时间】:2022-10-08 14:56:10
【问题描述】:

我正在修改我的 coreData 方法以使用 async 和 await 目前,但在理解 Swift 中的这些队列时仍然遇到问题, 也许你们可以在这里帮助我..

问题是,我想在函数“newUpdateMethod()”中处理每个步骤后收集反馈(logComment) 对我有用的唯一方法是将:“@MainActor”放在函数前面

据我了解,这意味着我强制该函数在主队列上运行,而不是在异步作业将运行的后台队列上运行。 由于那个主队列,我能够沿途填充变量“logComment”并将其返回给调用它的 VC.. 但我不确定100,如果这种方式真的有必要,那么必须有另一种方式来实现这一点...... 我试图将 logComment 语句放在 DispatchQueue.main.sync 块中以与主队列对话,但仍然: “并发执行代码中捕获的 var 'logComment' 的突变”

我的 coreData 方法

  1. json 获取方法
    import UIKit
    import CoreData
    
    class CoreDataInteraction {
    //(...)
         func getRemoteData() async throws -> [RemoteData] {
            var remoteData = [RemoteData]()
            
            do {
                let url = URL(string: "***")!
                let urlRequest = URLRequest(url: url)
                let (data, _) =  try await URLSession.shared.data(for: urlRequest)
                remoteData = try JSONDecoder().decode([RemoteData].self, from: data)
            }catch{
                let url = Bundle.main.url(forResource: "remoteData", withExtension: "json")    // use local data instead
                let urlRequest = URLRequest(url: url!)
                let (data, _) =  try await URLSession.shared.data(for: urlRequest)
                remoteData = try JSONDecoder().decode([RemoteWire].self, from: data)
                
                DispatchQueue.main.sync { Logging.insertError(message: error.localizedDescription, location: "get json data") }
            }
    
            return remoteData
        }
    
    1. 更新方法
        @MainActor  // really necessary ?
        func newUpdateMethod(completion: @escaping (String?) -> Void){
            var logComment:String = ""
            let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
            let request = RemoteData.fetchRequest() as NSFetchRequest<RemoteData>
            
            Task {
                do {
                    // Step 1
                    let jsonData = try await getRemoteData()
                        logComment = logComment + "\n Line 1"
                    // Step 2
                    var existingData = try context.fetch(request);
                        logComment = logComment + "\n Line 2"
                    // Step 3 comparison..
                    // ...
                        logComment = logComment + "\n Line 3"
                    // Step 4 ...
                    // continue . . .
                    
                    
                }
                completion(logComment)
            }
            
        }
    }
    
    

    这是调用该更新方法接收反馈并将其放入 UI 的 VC 函数

    class SettingsVC: UIViewController {
    //(...)
      @IBAction func updateButtonPressed(_ sender: Any) {
            //MARK: NEW Update processing
            self.LoadingStart()
            myCoreData.newUpdateMethod() {(success) in
                self.LoadingStop()
                self.updateTextField.font = UIFont.systemFont(ofSize: 12.0)
                self.updateTextField.text = success
                self.updateButton.isEnabled = false
            }
        }
    
    }
    

    我发现除了@MAinActor 之外没有其他方法可以让这件事发挥作用.. 必须有一种方法可以从任务/执行后台队列中获取变量的值,否则在我的理解中它是非常没用的 显然我在这里的理解中遗漏了一些东西:/

【问题讨论】:

  • 不相关,但我建议不要在从 bundle 读取数据时使用 URLSession。从本地文件系统读取时只需使用Data(contentsOf:)
  • 我还想知道为什么您仍在使用 newUpdateMethod 中的完成处理程序代码。 async-await 的整个想法是它使该模式变得不必要。
  • 如果您还没有看过,我可能会建议您查看 WWDC 2021 Bring Core Data concurrency to Swift and SwiftUI
  • thx @Rob 我刚刚看过那个视频,context.perform 对我来说不是一个选项,因为我的应用程序的目标 iOS 版本是 14 ..我有点理解这个话题,但我仍然遇到问题,采用这种新语法我自己的项目:(

标签: swift xcode asynchronous async-await


【解决方案1】:

我想我解决了它.. 看一看:

    func newUpdateMethod2() async -> String {
        var logComment:String = ""
        let context = await (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        let request = RemoteData.fetchRequest() as NSFetchRequest<RemoteData>
        
        let jsonData = try? await getRemoteData()
        guard jsonData != nil else {
            Logging.insertError(message: "error creating json source", location: "newUpdateMethod2")
            logComment = logComment + "⚠️  Error collecting json source"
            return logComment
        }
            
        var existingData = try? context.fetch(request)
        logComment = logComment + "
 Line 2(existing wires count: (existingData!.count))"
        
        return logComment
    }

这是调用部分:

    @IBAction func updateButtonPressed(_ sender: Any) {
        //MARK: NEW Update processing
        Task {
            do {
                self.LoadingStart()
                let feedback = await myCoreData.newUpdateMethod2()
                self.LoadingStop()
                self.updateTextField.font = UIFont.systemFont(ofSize: 12.0)
                self.updateTextField.text = feedback
                self.updateButton.isEnabled = false
                print("feedback = (feedback)")
            }
        }

...不再有完成处理程序:)

我不得不用guard而不是catch来帮助自己,因为我不想再次将所有代码包装在do {}语句中,并且发现没有其他方法可以在没有do {}的情况下使用catch .. 所以守卫正在做它的工作(y)

我想我想通了 :) thx 伙计们!

【讨论】:

    猜你喜欢
    • 2017-11-04
    • 1970-01-01
    • 2019-11-17
    • 1970-01-01
    • 2018-09-26
    • 2019-03-04
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多