function - 如何让函数 A 等到被调用的函数 B 完成后再继续

标签 function swift

用户在 map View 中。当在 map 上的某处长按时,将触发以下功能以设置新注释,包括正确的标题。

func action(gestureRecognizer: UIGestureRecognizer) {

    if gestureRecognizer.state == UIGestureRecognizerState.Began {

        var newTouch: CGPoint = gestureRecognizer.locationInView(self.mapView)

        var newCoordinate: CLLocationCoordinate2D = mapView.convertPoint(newTouch, toCoordinateFromView: self.mapView)

        var newLocation = CLLocation(latitude: newCoordinate.latitude, longitude: newCoordinate.longitude)

        var newAnnotation = MKPointAnnotation()

        newAnnotation.coordinate = newCoordinate

        CLGeocoder().reverseGeocodeLocation(newLocation, completionHandler: {(placemarks, error) in

            if error != nil { println(error) }

            let p: CLPlacemark = placemarks[0] as CLPlacemark

            var thoroughfare: String? = p.thoroughfare

            var subThoroughfare: String? = p.subThoroughfare

            if p.thoroughfare == nil || p.subThoroughfare == nil {

                var date = NSDate()

                newAnnotation.title = "Added \(date)"

            } else {

                newAnnotation.title = thoroughfare! + " " + subThoroughfare!

            }

        })

        self.mapView.addAnnotation(newAnnotation)

        self.mapView.selectAnnotation(newAnnotation, animated: true)

        places.append(["name":"\(newAnnotation.title)", "lat":"\(newCoordinate.latitude)", "lon":"\(newCoordinate.longitude)"])

    }

}

我知道将最后三行代码保留在 CLGeocoder block (闭包?)中时工作正常。但是,如果我将它们分开并在 }) 之后列出它们(或将一些代码放到另一个线程),我将面临异步运行的问题(因为我不明白如何控制async vs sync),当注释被添加到 map 并保存到地方时,CLGeocoder 还没有设置它的标题。 编程初学者会问:需要实现什么(disptach_sync...-某物)以便最后一行代码等待 CLGeocoder block 完成?我还没有设法以正确的方式实现这个命令......

最佳答案

您正确地指出,当您将这三行放在地理编码器的闭包中时它会起作用。因此,您应该准确地做到这一点:利用这种异步模式并将依赖于地理编码过程的所有内容放入闭包中。

在回答您的问题时,是的,有一些模式可以使异步任务以同步方式运行。例如,您可以使用调度组或调度信号量。但是您是从在主线程上运行的 IBAction 调用它的。而且你永远不想阻塞主线程。因此,任何使此地理编码请求从主线程同步运行的尝试都是不明智的。

此外,上述过程因 reverseGeocodeLocation 的问题而变得复杂:具体来说,闭包本身在主线程上运行,因此如果您阻塞主线程等待闭包完成,该应用程序将死锁。因此,上述模式甚至无法与 reverseGeocodeLocation 一起使用(如果从主线程完成)。

最重要的是,您应该接受异步模式,将依赖代码保留在闭包中。


顺便说一句,您的示例很简单,您只需将要执行的代码放在地理编码器的闭包中。但是,如果您想要两个单独的函数怎么办?例如,一个用于返回注释的地理编码,另一个用于将注释添加到 map 的函数。也许您期待这样的事情:

func handleLongPress(gesture: UILongPressGestureRecognizer) {
    if gesture.state == .Began {
        let location = gesture.locationInView(self.mapView)
        let annotation = geocodeLocationInMapView(self.mapView, location: location)
        addAnnotationToMapView(self.mapView, annotation: annotation)
    }
}

接下来的问题是您如何让第二个函数 addAnnotationToMapView 等待第一个函数 geocodeLocationInMapView 完成。同样,答案是“你不知道”。相反,就像 Apple 对其异步方法所做的那样,您将采用完成 block 模式:

func handleLongPress(gesture: UILongPressGestureRecognizer) {
    if gesture.state == .Began {
        let location = gesture.locationInView(self.mapView)

        geocodeLocationInMapView(self.mapView, location: location) { annotation, error in
            guard annotation != nil && error == nil else {
                print("error = \(error)")
                return
            }

            self.addAnnotationToMapview(self.mapView, annotation: annotation!)
        }
    }
}

在这种情况下,geocodeLocationInMapView 可能如下所示:

func geocodeLocationInMapView(mapView: MKMapView, location: CGPoint, completion: (MKAnnotation?, NSError?) -> ()) {
    let coordinate = mapView.convertPoint(location, toCoordinateFromView: mapView)
    let location = CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude)

    CLGeocoder().reverseGeocodeLocation(location) { placemarks, error in
        guard placemarks != nil && error == nil else {
            completion(nil, error)
            return
        }

        if let placemark = placemarks!.first {
            let annotation = MKPointAnnotation()
            annotation.coordinate = coordinate
            let thoroughfare = placemark.thoroughfare
            let subThoroughfare = placemark.subThoroughfare

            switch (thoroughfare, subThoroughfare) {
            case (nil, nil):
                annotation.title = "Added \(NSDate())"
            case (_, nil):
                annotation.title = thoroughfare
            default:
                annotation.title = thoroughfare! + " " + subThoroughfare!
            }

            completion(annotation, nil)
        }
    }
}

addAnnotationToMapview 可能如下所示:

func addAnnotationToMapview(mapView: MKMapView, annotation: MKAnnotation) {
    mapView.addAnnotation(annotation)
    mapView.selectAnnotation(annotation, animated: true)

    places.append(["name":"\(annotation.title)", "lat":"\(annotation.coordinate.latitude)", "lon":"\(annotation.coordinate.longitude)"])
}

诚然,这是一个人为的例子,我建议您像我在本答案开头描述的那样处理您的 IBAction。但是为了回答更广泛的问题“我如何让函数 A 等到函数 B 完成”,您可以使用此处概述的完成 block 模式。

关于function - 如何让函数 A 等到被调用的函数 B 完成后再继续,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25634068/

相关文章:

html - 带有表情符号的HTML字符串Swift

你能假设类型转换指针是安全的吗?

matlab - 如何使用匿名函数实现不同操作的向量输入/输出?

ios - collectionview admob ios 中的横幅广告

swift - 删除 CollectionView iOS 中的间距

swift - 找出内存泄漏

swift - UITableview 中的收藏夹按钮 (Swift 4)

c++ - atol()、atof()、atoi() 函数行为,是否有稳定的方法从/到字符串/整数转换?

c++ - 使用类和函数来提供有关矩形的信息

javascript - 如何阻止 javascript 中的警报在页面加载时触发,并使其在单击按钮时运行?