简体   繁体   English

Swift 并发在第一次加载时失败

[英]Swift concurrency failing on first load

I have a concurrent function running, that is connected to a UIBarbuttonItem我有一个并发函数正在运行,它连接到UIBarbuttonItem

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

Although I have run into a weird issue.虽然我遇到了一个奇怪的问题。 Running any task (as simple or complex) will not run on the very first initialisation of the view controller.运行任何任务(无论是简单的还是复杂的)都不会在视图控制器的第一次初始化时运行。 Moving the code outside the Task runs as expected.将代码移到Task之外按预期运行。 Running the function, leaving the view, and tapping the button again will successfully disable the buttons.运行函数,离开视图,再次点击按钮将成功禁用按钮。

Print statements work but any code falls into the void.打印语句有效,但任何代码都落入了空白。 View Controller isn't constructed any differently than usual. 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)
        }
    }
}

-- Update MLClassifier.swift -- 更新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")
}

After some more semi-sleepless nights, I found the solution.经过几个半睡不醒的夜晚,我找到了解决办法。

Rather than having it like this;而不是这样;

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
    }

You need to have it like this: Throwing the handler.perform inside the closure seems to have been the missing puzzle.你需要这样:在闭包内抛出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