【问题标题】:iOS - Swift - Function that returns asynchronously retrieved valueiOS - Swift - 返回异步检索值的函数
【发布时间】:2023-03-19 06:54:01
【问题描述】:

所以我有一个来自 Parse 的 PFFile 对象,我正在尝试创建一个函数来检索该 PFFile 的 UIImage 表示并返回它。比如:

func imageFromFile(file: PFFile) -> UIImage? {
    var image: UIImage?

    file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in
        if error != nil {
            image = UIImage(data: data!)
        }
    }

    return image
}

但是,这里的问题很明显。我每次都会得到 nil,因为 getDataInBackroundWithBlock 函数是异步的。有什么方法可以等到 UIImage 被检索到之后才返回图像变量?我不知道在这种情况下使用同步 getData() 是否是一种有效的方法。

【问题讨论】:

  • 可以等待(可以使用`NSCondition),但如果你的调用代码在主线程上,那就不好了。通常的策略是在完成处理程序中对图像做一些事情。
  • 如果你等待,那么它就不会是异步的。

标签: ios swift asynchronous parse-platform


【解决方案1】:

是的,可以这样做。它称为closure,或更常见的是callbackcallback 本质上是一个函数,您可以在其他函数中将其用作参数。参数的语法是

functionName: (arg0, arg1, arg2, ...) -> ReturnType

ReturnType 通常是Void。在您的情况下,您可以使用

result: (image: UIImage?) -> Void

调用带有一个回调的函数的语法是

function(arg0, arg1, arg2, ...){(callbackArguments) -> CallbackReturnType in
    //code
}

并且调用带有多个回调的函数的语法是(缩进以便于阅读)

function(
    arg0, 
    arg1,
    arg2,
    {(cb1Args) -> CB1Return in /*code*/},
    {(cb2Args) -> CB2Return in /*code*/},
    {(cb3Args) -> CB3Return in /*code*/}
)

如果你的回调函数对主函数进行转义(主函数返回后调用回调),必须在回调的参数类型前加上@escaping

您将希望使用一个回调函数,该回调函数将在函数返回后调用,结果包含 UIImage?

所以,您的代码可能看起来像这样

func imageFromFile(file: PFFile, result: @escaping (image: UIImage?) -> Void){
    var image: UIImage?

    file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in
        //this should be 'error == nil' instead of 'error != nil'. We want
        //to make sure that there is no error (error == nil) before creating 
        //the image
        if error == nil {
            image = UIImage(data: data!)
            result(image: image)
        }
        else{
            //callback nil so the app does not pause infinitely if 
            //the error != nil
            result(image: nil)
        }
    }
}

要调用它,你可以简单地使用

imageFromFile(myPFFile){(image: UIImage?) -> Void in
    //use the image that was just retrieved
}

【讨论】:

  • 我知道闭包是什么 ;) 我在想更多关于线程阻塞器的东西,但我对它们了解不多,所以我不知道这在这种情况下是否相关.在这里使用闭包似乎有点复杂,因为我想做的只是返回图像以供将来使用,但我想这是这样做的方法。
  • @itstrueimryan 您可以将图像放入字典中以备将来使用,但我觉得这可能是最好的方法。如果你想改变使用图像的方式或时间,使用闭包就可以了,因此你必须更改更少的代码
  • @itstrueimryan - 是的,这种完成关闭模式是最好的。从技术上讲,您可以阻塞线程,直到网络请求完成(例如,使用信号量、条件等),但这是一种可怕的模式(如果您需要一个原因列表,说明它是一个坏主意,让我们知道)。如果您正在寻找其他模式,它们包括具有依赖关系的异步 NSOperation 子类(更多代码,但在非常复杂的场景中很有用)、委托协议模式(更麻烦,恕我直言)或期货/承诺(非标准, 用于编写异步代码的第三方模式)。
  • 现在重要的是制作结果:@escaping (image: UIImage?) -> Void 因为默认情况下完成闭包是非转义闭包。
【解决方案2】:

您想要的正是承诺/未来设计模式所做的。 Swift 中有很多实现。我将以出色的 BrightFutures 库为例。 (https://github.com/Thomvis/BrightFutures)

代码如下:

func imageFromFile(file: PFFile) -> Future<UIImage> {
    let promise = Promise<UIImage>()

    file.getDataInBackgroundWithBlock() { (data: NSData?, error: NSError?) -> Void in
        if error != nil {
            image = UIImage(data: data!)

            // As soon as the method completes this will be called
            // and triggers the future.onSuccess in the caller.
            promise.success(image)

        } else {

            // This would trigger future.onFailure in the caller
            promise.failure(error)
        }
    }

    return promise.future
}

解释:你基本上做的是创造一个“承诺”,在“未来”会有结果。在异步方法完成之前,您将立即返回这个未来承诺。

这个方法的调用者会这样处理它:

func doSomethingWithTheImage() {
    let future = imageFromFile(file: pffile)

    future.onSuccess { image in
        // do something with UIImage: image
    }

    future.onFailure { error in
        // handle NSError: error
    }
}

在 onSuccess 处理程序中,您正在使用成功下载的图像执行所有操作。如果出现错误,您将在 onFailure 处理程序中处理它。

这解决了返回“nil”的问题,也是处理异步进程的最佳实践之一。

【讨论】:

    【解决方案3】:

    我不建议这样做

    我为 Swift 2.0 编写了一个小型库,它可以在同步和异步方法之间进行转换,这正是您所要求的。可以找到here。请务必阅读免责声明,其中解释了在哪些情况下可以使用它(很可能不适用于您的情况)

    你可以这样使用它:

    func imageFromFile(file: PFFile) throws -> UIImage? {
        let syncGet = toSync(file.getDataInBackgroundWithBlock)
        let data = try syncGet()
        return data.map(UIImage.init)
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 2017-10-18
      • 1970-01-01
      • 2015-08-24
      • 1970-01-01
      相关资源
      最近更新 更多