2016-03-23 7 views
0

Ich bin ziemlich neu bei swift und habe einige Nachforschungen darüber gemacht, wie ich diese Frage selbst beantworten kann, da ich lernen will, aber ich bin völlig ratlos.Swift-Code wird asynchron ausgeführt, auch während in Completion-Handler

Ich habe eine Funktion, die Daten von einem Server anfordert, und nachdem die Daten empfangen wurden, wird ein Beendigungshandler ausgeführt, der die Daten analysiert. Innerhalb des zuvor erwähnten Completion-Handlers wird eine andere Funktion aufgerufen, der ein Completion-Handler selbst übergeben wird.

Aus irgendeinem Grund wird der Funktionsaufruf innerhalb der Funktion übersprungen und beendet, nachdem der erste Beendigungshandler vollständig ausgeführt wurde. Dies könnte unter sinnvoller mit dem Code machen:

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


} 

ich die print-Anweisungen gegeben, die „YOOO“ und „HEYYY“ drucken nur so konnte ich sehen, was zuerst ausgeführt wurde, und „HEYY“ ist immer an erster Stelle. Ich muss nur sicherstellen, dass die Geocodierung immer stattfindet, bevor das Objekt persistiert. Ich sah einen Stackoverflow post, der objc_sync_enter (self) für synchronen Betrieb erwähnt, aber ich bin nicht einmal sicher, ob es das ist, was ich brauche.

Dies ist die Funktion, die die Website geocodiert (einhüllen es hilft):

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

Antwort

1

denke ich das Verhalten Ihres Sehens erwartet wird. Sie haben zwei Ebenen der asynchronen Methoden:

requestSites 
    geoCodeSite 

Da die geoCodeSite Verfahren auch asynchron ist, seine callback gut nach der Zeile ausgeführt wird:

MyData.persistInspectionSite(site) 

Also Ihr Problem ist, wie bis alle warten InspectionSite s haben geocodiert, bevor Sie die Site beibehalten, nicht wahr?

Dispatch-Gruppen können verwendet werden, um zu erkennen, wenn mehrere asynchrone Ereignisse beendet sind, siehe meine Antwort here.

Wie Versand Gruppen

dispatch_groups werden verwendet, um Implementieren einen Rückruf zu feuern, wenn mehrere asynchrone Rückrufe beendet haben. In Ihrem Fall müssen Sie warten, bis alle asynchronen geoCodeSite Callbacks abgeschlossen sind, bevor Sie Ihre Site beibehalten.

Erstellen Sie also eine Dispatch-Gruppe, feuern Sie Ihre geoCodeSite Anrufe ab, und implementieren Sie den Dispatch-Callback, in dem Sie Ihre geokodierten Sites beibehalten können.

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

Vergessen Sie nicht,

dispatch_group_leave(myGroup) 

innerhalb der Schließung von geoCodeSite hinzuzufügen! Andernfalls weiß dispatch_group nie, wann Ihr Async-Aufruf beendet ist.

+0

hmmm so im Überprüfung Ihrer anderen Antwort und ich versuche zu implementieren, was Sie dort in meine Funktion haben. Wenn du eine Sekunde Zeit hast, könntest du mir ein bisschen weiter in die richtige Richtung zeigen, wie ich das in meinen Code einbauen könnte? –

+0

Super, danke für den Schnitt. Das war die Lösung, die ich brauchte. –