简体   繁体   English

iOS - Swift - 返回异步检索值的函数

[英]iOS - Swift - Function that returns asynchronously retrieved value

So I have a PFFile object from Parse, and I'm trying to create a function that retrieves the UIImage representation of that PFFile and returns it.所以我有一个来自 Parse 的 PFFile 对象,我正在尝试创建一个函数来检索该 PFFile 的 UIImage 表示并返回它。 Something like:就像是:

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
}

However, the problem here is obvious.然而,这里的问题是显而易见的。 I'm going to get nil every time because the getDataInBackroundWithBlock function is asynchronous.我每次都会得到 nil,因为 getDataInBackroundWithBlock 函数是异步的。 Is there any way to wait until the UIImage has been retrieved before the image variable is returned?在返回图像变量之前,有没有办法等到 UIImage 被检索? I don't know if using the synchronous getData() is an efficient way to go in this case.我不知道在这种情况下使用同步 getData() 是否是一种有效的方法。

Yes, it is possible to do this.是的,可以这样做。 Its called a closure , or more commonly a callback .它称为closure ,或更常见的callback A callback is essentially a function that you can use as an argument in another functions. callback本质上是一个函数,您可以将其用作另一个函数中的参数。 The syntax of the argument is参数的语法是

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

ReturnType is usually Void . ReturnType通常是Void In your case, you could use在你的情况下,你可以使用

result: (image: UIImage?) -> Void

The syntax of calling a function with one callback in it is调用带有一个回调函数的函数的语法是

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

And the syntax of calling a function with several callbacks is (indented to make it easier to read)使用多个回调调用函数的语法是(缩进以使其更易于阅读)

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

If your callback function escapes the main function (the callback is called after the main function returns), you must add @escaping in front of the callback's argument type如果你的回调函数转义了 main 函数(回调在 main 函数返回后被调用),你必须在回调的参数类型前添加@escaping

You're going to want to use a single callback that will be called after the function returns and that contains UIImage?您将要使用一个回调,该回调将在函数返回后调用并且包含UIImage? as the result.作为结果。

So, your code could look something like this所以,你的代码可能看起来像这样

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)
        }
    }
}

And to call it, you could simply use并调用它,你可以简单地使用

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

What you want is exactly what the promise/future design pattern does.您想要的正是承诺/未来设计模式所做的。 There are many implementations in Swift. Swift 中有很多实现。 I will use as an example the excellent BrightFutures library.我将使用优秀的 BrightFutures 库作为示例。 ( https://github.com/Thomvis/BrightFutures ) ( https://github.com/Thomvis/BrightFutures )

Here the code:这里的代码:

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
}

Explanation: what you basically do is creating a "promise" that there will be a result in the "future".解释:你基本上做的是创造一个“承诺”,即“未来”会有结果。 And you are returning this future-promise immediately, before the async method completes.在异步方法完成之前,您将立即返回这个未来的承诺。

The caller of this method will handle it like this:这个方法的调用者会像这样处理它:

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

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

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

In the onSuccess handler you are doing all the stuff with the successfully downloaded image.在 onSuccess 处理程序中,您正在使用成功下载的图像执行所有操作。 If there is an error you handle it in the onFailure handler.如果出现错误,则在 onFailure 处理程序中处理它。

This solves the problem of returning "nil" and is also one of the best practices to handle asynchronous processes.这解决了返回“nil”的问题,也是处理异步进程的最佳实践之一。

I do not recommend this我不推荐这个

I have written a small library for Swift 2.0 which can convert between synchronous and asynchronous methods, which would be what you're asking for.我为 Swift 2.0 编写了一个小型库,它可以在同步和异步方法之间进行转换,这正是您所要求的。 It can be found here .可以在这里找到。 Be sure to read the disclaimer which explains in which scenarios it's okay to use it (most likely not in your case)请务必阅读免责声明,其中解释了在哪些情况下可以使用它(很可能不是您的情况)

You'd be able to use it like this:你可以像这样使用它:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM