簡體   English   中英

Swift 並發在第一次加載時失敗

[英]Swift concurrency failing on first load

我有一個並發函數正在運行,它連接到UIBarbuttonItem

.init(barButtonSystemItem: .camera, target: self, action: #selector(runClassification(_:))

雖然我遇到了一個奇怪的問題。 運行任何任務(無論是簡單的還是復雜的)都不會在視圖控制器的第一次初始化時運行。 將代碼移到Task之外按預期運行。 運行函數,離開視圖,再次點擊按鈕將成功禁用按鈕。

打印語句有效,但任何代碼都落入了空白。 View Controller 的構造與平常沒有任何不同。

@objc func runClassification(_ sender: UIBarButtonItem) {
    Task {
// This runs
        navigationItem.rightBarButtonItems?.forEach { $0.isEnabled = false }
        guard let image = imageView.image else { return }
        do {
            let response = try await MLClassifer.sharedManager.updateClassifications(capturedImage: image)
// None of this runs on first call.
            print("MLClassifer.sharedManager.updateClassifications begining")
        } catch {
            debugPrint(error)
        }
    }
}

-- 更新MLClassifier.swift


private typealias ClassifierCheckedContinuation = CheckedContinuation<[VNRecognizedObjectObservation], Error> // 1
private var classifierContinuation: ClassifierCheckedContinuation?

@MainActor
func updateClassifications(capturedImage: UIImage) async throws -> [VNRecognizedObjectObservation] {
    self.capturedImage = capturedImage
    let orientation = CGImagePropertyOrientation(capturedImage.imageOrientation)
    
    guard let ciImage = CIImage(image: capturedImage) else {
        throw MLClassiferError.invalidConversion
    }
    
    guard let model = self.model else {
        throw MLClassiferError.invalidModel
    }
    
    let mlModel = try VNCoreMLModel(for: model)
    
    let handler = VNImageRequestHandler(ciImage: ciImage, orientation: orientation)
    
    let request = VNCoreMLRequest(model: mlModel) { request, error in
        self.processClassifications(for: request, error: error)
    }
    
    try handler.perform([request])
    
    return try await withCheckedThrowingContinuation { (continuation: ClassifierCheckedContinuation) in
        self.classifierContinuation = continuation
    }
}

func processClassifications(for request: VNRequest, error: Error?) {
    if let error = error {
        classifierContinuation?.resume(throwing: error)
        return
    }

    guard let results = request.results, !results.isEmpty else {
        classifierContinuation?.resume(throwing: MLClassiferError.invalidConversion)
        return
    }
        
    guard let classifications = results as? [VNRecognizedObjectObservation], !classifications.isEmpty else {
        classifierContinuation?.resume(throwing: MLClassiferError.invalidConversion)
        return
    }
    
    guard let image = self.capturedImage,
          let capturedImage = CIImage(image: image)
    else {
        classifierContinuation?.resume(throwing: MLClassiferError.invalidConversion)
        return
    }

    classifierContinuation?.resume(returning: classifications)
    print("MLClassifer.sharedManager.updateClassifications returning")
}

經過幾個半睡不醒的夜晚,我找到了解決辦法。

而不是這樣;

let request = VNCoreMLRequest(model: mlModel) { request, error in
        self.processClassifications(for: request, error: error)
    }
    
    try handler.perform([request])
    
    return try await withCheckedThrowingContinuation { (continuation: ClassifierCheckedContinuation) in
        self.classifierContinuation = continuation
    }

你需要這樣:在閉包內拋出handler.perform似乎是缺少的難題。

return try await withCheckedThrowingContinuation { continuation in
    self.classifierContinuation = continuation
    
    let request = VNCoreMLRequest(model: mlModel) { request, error in
        self.processClassifications(for: request, error: error)
    }

    try? handler.perform([request])
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM