ios - 即使在完成处理程序中,Swift 代码也会异步执行

标签 ios swift closures synchronous

我对 swift 相当陌生,自从我想学习以来,我一直在研究如何自己回答这个问题,但我完全被难住了。

我有一个从服务器请求数据的函数,收到数据后,执行一个完成处理程序来解析数据。在前面提到的完成处理程序中,调用另一个函数,该函数会传递一个完成处理程序本身。

由于某种原因,函数内的函数调用被跳过,并在第一个完成处理程序完全执行后完成。使用下面的代码可能更有意义:

func loadSites(forceDownload: Bool){
    self.inspectionSites = MyData.getLocallyStoredInspectionSites()
    if self.inspectionSites.count < 1 || forceDownload {

        self.http.requestSites({(sitesAcquired, jsonObject) -> Void in
            guard sitesAcquired else{
                SwiftOverlays.removeAllBlockingOverlays()
                MyAlertController.alert("Unable to acquire sites from server or locally")
                return
            }
            let result = jsonObject

            for (_,subJson):(String, JSON) in result!.dictionaryValue {
                let site = InspectionSite()
                site.name = subJson[self.currentIndex]["name"].string!
                site.city = subJson[self.currentIndex]["city"].string!
                site.address = subJson[self.currentIndex]["address"].string!
                site.state = subJson[self.currentIndex]["state"].string!
                site.zip = subJson[self.currentIndex]["zip"].stringValue
                site.siteId = subJson[self.currentIndex]["id"].string!

                objc_sync_enter(self) //SAW A STACKOVERFLOW POST WITH THIS, THOUGHT IT MIGHT HELP
                MyLocation.geoCodeSite(site, callback:{(coordinates) -> Void in
                    print("YO!!!! GEOCODING SITE!")
                    self.localLat = coordinates["lat"]!
                    self.localLon = coordinates["lon"]!
                })
                objc_sync_exit(self)

                for type in subJson[self.currentIndex]["inspection_types"]{
                    let newType = InspectionType()
                    newType.name = type.1["name"].string!
                    newType.id = type.1["id"].string!
                    site.inspectionTypes.append(newType)
                }



                site.lat = self.localLat
                print("HEYY!!!! ASSIGNING COORDS")
                site.lon = self.localLon
                let address = "\(site.address), \(site.city), \(site.state) \(site.zip)"
                site.title = site.name
                site.subtitle = address
                MyData.persistInspectionSite(site)
                self.currentIndex++
            }

            self.inspectionSites = MyData.getLocallyStoredInspectionSites()
            SwiftOverlays.removeAllBlockingOverlays()
            self.showSitesOnMap(self.proteanMap)
        })


    }else{
        SwiftOverlays.removeAllBlockingOverlays()
        self.showSitesOnMap(self.proteanMap)
    }


}

我添加了那些打印“YOOO”和“HEYYY”的打印语句,这样我就可以看到首先执行的内容,并且“HEYY”始终是第一个。我只需要确保地理编码始终在对象持久化之前发生。我看到一篇 stackoverflow 帖子提到了 objc_sync_enter(self) 用于同步操作,但我什至不确定这是否是我所需要的。

这是对网站进行地理编码的函数(如果它有帮助的话):

    class func geoCodeSite(site: InspectionSite, callback: ((coordinates: Dictionary<String, String>)->Void)?) {
    let geocoder = CLGeocoder()

    let address: String = "\(site.address), \(site.city), \(site.state) \(site.zip)"
    print(address)
    geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
        if((error) != nil){
            print("Error", error)
        }
        if let placemark = placemarks?.first {
             MyLocation.mLat = String(stringInterpolationSegment:placemark.location!.coordinate.latitude)
             MyLocation.mLon = String(stringInterpolationSegment:placemark.location!.coordinate.longitude)
             MyLocation.coordinates = ["lat":mLat, "lon":mLon]
            print(MyLocation.coordinates)
             callback?(coordinates: MyLocation.coordinates)
        }
    })
}

最佳答案

我认为您看到的行为是意料之中的。您有两个级别的异步方法:

requestSites
    geoCodeSite

因为 geoCodeSite 方法也是异步的,所以它的 callback 在行之后很好地执行:

MyData.persistInspectionSite(site)

所以您的问题是如何等到所有 InspectionSite 都完成地理编码后再保留站点,对吗?

调度组可用于检测多个异步事件何时完成,请参阅我的回答 here .

如何实现调度组

dispatch_groups 用于在多个异步回调完成时触发回调。在您的情况下,您需要等待所有 geoCodeSite 异步回调完成,然后才能保留您的网站。

因此,创建一个调度组,触发您的 geoCodeSite 调用,并实现调度回调,您可以在其中保留您的地理编码站点。

var myGroup = dispatch_group_create()
dispatch_group_enter(myGroup)
...
fire off your geoCodeSite async callbacks
...
dispatch_group_notify(myGroup, dispatch_get_main_queue(), {
    // all sites are now geocoded, we can now persist site
})

不要忘记添加

dispatch_group_leave(myGroup)

在 geoCodeSite 的闭包里面!否则 dispatch_group 将永远不知道您的异步调用何时结束。

关于ios - 即使在完成处理程序中,Swift 代码也会异步执行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36168541/

相关文章:

ios - Swift inputAccessoryView 覆盖错误

php - 在闭包中使用父函数参数

swift - 在 super.init 中引用自己

ios - UITableView sectionForSectionIndexTitle 方法不会在 iOS 中滚动 tableview

ios - 如何隐藏 UITableViewCell?请看截图

ios - Realm 洗牌 RLMArray

swift - 为什么 FileHandle 不一致地返回 'nil'

swift - 在 swift 中使用 strongSelf 的正确方法是什么?

ios - UITableViewCell 占位符图像

ios - 即使在使用弱/强 ARC 语义时也保留循环