简体   繁体   English

Swift:使用异步方法获取价值

[英]Swift: get value using async method

I'm looking for a good idiom or two for this situation: 我正在为这种情况寻找一两个好的习惯用法:

I want to convert a CLLocationCoordinate2D to a CLPlacemark with an asynchronous reverse geolocation call, as part of a sequence of other operations. 我想通过异步反向地理位置调用将CLLocationCoordinate2D转换为CLPlacemark,作为其他操作序列的一部分。

The conversion step is very much a "utility" step, so putting a lot of code in the handler to do the "other operations" feels like poor structure. 转换步骤实际上是一个“实用程序”步骤,因此将大量代码放入处理程序中以执行“其他操作”感觉就像是结构不良。

I can store the result in a class variable but then I need to know when the async step has completed, which means some kind of event trigger or queueing of the main thread with timeouts or something, which also seem awkward. 我可以将结果存储在类变量中,但是然后我需要知道异步步骤何时完成,这意味着某种事件触发或主线程排队以及超时或其他原因,这似乎也很尴尬。

Is there a standard approach to this? 有标准的方法吗? Is it common to just put the code in the handler? 仅将代码放入处理程序中是否常见?

Thanks! 谢谢!

Here's the specific code for my context, FWIW. 这是我的上下文的特定代码FWIW。

func getPlaceFromCoordinate(coordinate: CLLocationCoordinate2D) -> CLPlacemark? {

    var loc = CLLocation(
        latitude: coordinate.latitude,
        longitude: coordinate.longitude
    )

    var mightBeAPlace: CLPlacemark? = nil

    CLGeocoder().reverseGeocodeLocation(loc, completionHandler: {(placemarks, error) -> Void in
        if(error != nil) {
            println("Reverse geocoding error.")
        }
        else if (placemarks.count == 0) {
            println("no placemarks")
        }
        else { // if (placemarks.count > 0)
            println("we have placemarks")
            mightBeAPlace = CLPlacemark(placemark: placemarks[0] as! CLPlacemark)
            println("Inside closure place: \(mightBeAPlace?.locality)")
            lastUserSelectedPlace = mightBeAPlace // This stores it in a class variable.
        }
    })
    println("Outside closure place: \(mightBeAPlace?.locality)")
    return mightBeAPlace // This of course fails because the async task is running separately.
}

The typical approach is to adopt the completionHandler approach yourself, eg: 典型的方法是自己采用completionHandler方法,例如:

lazy var geocoder = CLGeocoder()

func getPlaceFromCoordinate(coordinate: CLLocationCoordinate2D, completionHandler: (CLPlacemark!, NSError?) -> ()) {
    let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)

    geocoder.reverseGeocodeLocation(location) { placemarks, error in
        if error != nil {
            println("Reverse geocoding error: \(error)")
        } else if placemarks.count == 0 {
            println("no placemarks")
        }

        completionHandler(placemarks.first as? CLPlacemark, error)
    }
}

And you'd call it like so: 您可以这样称呼它:

getPlaceFromCoordinate(coordinate) { placemark, error in 
    if placemark != nil {
        // use placemark here
    }
}

// but do not use it here, because the above runs asynchronously (i.e. later)

In terms of how much code you put in this completionHandler closure, and how much you put in getPlaceFromCoordinate , that's entirely a function of what that code entails. 就您在该completionHandler闭包中放入多少代码以及在getPlaceFromCoordinate放入多少代码getPlaceFromCoordinate ,这完全是该代码所需功能的函数。 But as much routine code that is repeated (eg logging of errors, what have you) inside the getPlaceFromCoordinate , and hopefully the closure will be limited to taking the CLPlacemark and updating model objects and/or UI. 但是,在getPlaceFromCoordinate内重复执行的许多常规代码(例如,记录错误,您所拥有的内容)以及希望关闭将限于使用CLPlacemark并更新模型对象和/或UI。

But, yes, the convention is to put anything contingent upon the completion of the asynchronous method inside the completion handler. 但是,是的,约定是将任何取决于异步方法完成的内容放入完成处理程序中。 While there are techniques to make this asynchronous method behave synchronously, that's generally a very bad idea. 尽管有使这种异步方法同步运行的技术,但这通常是一个非常糟糕的主意。

If you're finding that the code inside the closure is getting unwieldy, then engage in functional decomposition and move this code into its own function and have the completion handler simply call that. 如果发现闭包中的代码变得笨拙,请进行函数分解,然后将此代码移至其自己的函数中,并让完成处理程序简单地调用它。 Or there are other asynchronous patterns, too (eg asynchronous NSOperation subclasses with dependencies between them, promises/futures, etc.). 或也有其他异步模式(例如,异步NSOperation子类之间具有依赖关系,承诺/未来等)。 But use an asynchronous pattern. 但是使用异步模式。

The approach I decided on is to write the getPlaceFromCoordinate function to accept an optional closure, so the calling method can control what's done with the result of the lookup. 我决定采用的方法是编写getPlaceFromCoordinate函数以接受可选的闭包,因此调用方法可以控制查找结果的处理方式。

func getPlaceFromCoordinate(
        coordinate: CLLocationCoordinate2D,
        placeAction: ((CLPlacemark) -> Void)?
    ) {

        :
    // Reverse geocode.
        :

    //  If we get a good placemark:
    if (placeAction != nil) {
        placeAction!(placemark)
    }

    }

This seems suitably straightforward for the context, flexible and puts the calling code back "in the drivers seat". 对于上下文来说,这似乎很合适,很灵活,并且将调用代码放回“驾驶员座位”中。 Not sure what other pros or cons there might be. 不知道还有什么其他利弊。

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

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