2

谁能告诉我为什么从下面的函数NSAlert.runModal()内部调用时会崩溃。Task.init {}

Thread 1: EXC_BREAKPOINT (code=1, subcode=0x1a4122a18)

日志报告说这是在主线程上运行的,那么为什么会崩溃呢?

如果我把它包起来,DispatchQueue.main.async { }那么它工作正常。

isBusy 属性绑定到启动按钮的启用状态,这不会抱怨从后台线程调用。

Task.init {}这是一个错误还是我在使用将异步函数包装在 a 中时做错了viewController什么?

class OSViewController: NSViewController {
    
    @IBOutlet weak var ringBar: OSPlainCircularProgressBar!
    
    @IBOutlet weak var upBar: OSProgressBar!
    @IBOutlet weak var downBar: OSProgressBar!
    
    @objc dynamic var progress: Double = 0.0 {
        didSet {
            ringBar.progress = progress / 100.0
            upBar.progress = progress / 100.0
            downBar.progress = progress / 100.0
        }
    }
    
    @objc dynamic var isBusy = false
    
    let asyncObj = AsyncObject(name: "Some Object")
    
    var progressCallback: (Double)->Void {
        return {progress in
            DispatchQueue.main.async { self.progress = progress }
        }
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Do any additional setup after loading the view.
    }
    
    override var representedObject: Any? {
        didSet {
            // Update the view, if already loaded.
        }
    }
    
    @IBAction func start(_ sender: Any) {
        
        isBusy = true
        log("start")
        
        
        Task.init {
            
            log("sync start")
            let (result, message) = await asyncObj.process(5, progress: progressCallback)
            isBusy = false
            log(message)
            log("sync end")  // Reports as running in main thread
            
            
            // CRASHES HERE !
            let alert = self.dialogOK(question: "Some question", text: "Some thing else")
            
            
            
            
            log("task end") // Reports as running in main thread
        }
        
    }
    
    func dialogOK(question: String, text: String) -> Bool {
        let alert = NSAlert()
        alert.messageText = question
        alert.informativeText = text
        alert.alertStyle = .warning
        alert.addButton(withTitle: "OK")
        return alert.runModal() == .alertFirstButtonReturn
    }

    /// This process MUST run on a background thread - it cannot run on the main thread
    /// So how do I force it to run on a background thread ?
    func process(_ count: Int, progress: @escaping (Double)->Void) async -> (Bool, String)  {
        
        // Still on the main thread here ??
        log("1 First part on the main thread ??")
        
        let task = Task.detached { () -> (Bool, String) in
            
            //Thread.sleep(forTimeInterval: 0.1)
            log("2 Run a loop in the background")
            log("  Thread: \(Thread.isMainThread ? "UI" : "BG")")
            
            var cnt = 0
            for i in 0..<count {
                Thread.sleep(forTimeInterval: 0.025)// sleep(seconds: 0.1)
                log("  i: \(i)")
                cnt += 1
                progress (Double(cnt)/Double(count)*100)
                
            }
            log("  >>>>")
            
            return (true, "Background task Done")
            
        }
        
        let result = await task.value
        
        log("2 First part on the main thread ??")
        return result
        
    }
    

    
}

func log(_ msg: String) {
    
    let type = Thread.isMainThread ? "UI" : "BG"
    
    print("\(type): \(msg)")
}

extension OSViewController {
    func sleep(seconds: Double) async {
        await Task.sleep(UInt64(seconds * 1000000000.0))
    }
}

class AsyncObject {
    var name: String = "Someobject"
    
    init(name: String){
        self.name = name
    }
    
    /// This process MUST run on a background thread - it cannot run on the main thread
    /// So how do I force it to run on a background thread ?
    @MainActor func process(_ count: Int, progress: @escaping (Double)->Void) async -> (Bool, String)  {
        
        // Still on the main thread here ??
        log("1 First part on the main thread ??")
        log("  Object is \(self.name)")
        
        let task = Task.detached { () -> (Bool, String) in
            
            //Thread.sleep(forTimeInterval: 0.1)
            log("2 Run a loop in the background")
            log("  Thread: \(Thread.isMainThread ? "UI" : "BG")")
            
            var cnt = 0
            for i in 0..<count {
                Thread.sleep(forTimeInterval: 0.025)// sleep(seconds: 0.1)
                log("  i: \(i)")
                cnt += 1
                progress (Double(cnt)/Double(count)*100)
                
            }
            log("  >>>>")
            
            return (true, "Background task Done")
            
        }
        
        let result = await task.value
        
        log("2 First part on the main thread ??")
        return result
        
    }
}

编辑:这似乎有效

@IBAction func start(_ sender: Any) {
    
    isBusy = true
    log("start")
    
    
    Task.init {
        
        log("sync start")
        let (result, message) = await asyncObj.process(5, progress: progressCallback)
        isBusy = false
        log(message)
        log("sync end")  // Reports as running in main thread
        
        
        // NO CRASHES HERE !
        self.alert = self.dialogOK(question: "Some question", text: "Some thing else")
        
        if let win = self.view.window {
            self.alert?.beginSheetModal(for: win) {result in
                self.alert = nil
            }
        }
        
        
        
        log("task end") // Reports as running in main thread
    }
    
}
4

0 回答 0