Swift 即使在完成处理程序中,代码也会异步执行
Swift code being executed asynchronously even while in completion handler
我是 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 Whosebug 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" 总是最先执行的。我只需要确保地理编码总是在对象被持久化之前发生。我看到一个 Whosebug post,其中提到 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
s 在保留站点之前进行地理编码,对吗?
调度组可用于检测多个异步事件何时完成,请参阅我的回答。
如何实现调度组
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 永远不会知道您的异步调用何时完成。
我是 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 Whosebug 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" 总是最先执行的。我只需要确保地理编码总是在对象被持久化之前发生。我看到一个 Whosebug post,其中提到 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
s 在保留站点之前进行地理编码,对吗?
调度组可用于检测多个异步事件何时完成,请参阅我的回答
如何实现调度组
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 永远不会知道您的异步调用何时完成。