简体   繁体   中英

Swift semaphore not waiting for function to finish before calling UI

I'm trying to delay a segue until I get a response from a reverseGeocodeLocation call. However, when using breakpoints to check when the value actually changes, it's still happening after the UI transition occurs. I've tried having the function be a void and with the current String return.

EDITED CODE:

func getReversedGeocodeLocation(completionHandler: (String, NSError?) ->()) {

    let semaphore = dispatch_semaphore_create(0)

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in

        CLGeocoder().reverseGeocodeLocation(self.newMeetupLocation, completionHandler: {(placemarks, error) -> Void in

            if error != nil {
                print("Reverse geocoder failed with error" + error!.localizedDescription)
                return
            }
            else if placemarks?.count > 0 {

            }
            else {
                print("Problem with the data received from geocoder")
            }

            completionHandler(placemarks!.first!.name! ?? "", error)
        })

        dispatch_semaphore_signal(semaphore)

    }
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}

OLD CODE:

let semaphore = dispatch_semaphore_create(1) //even with 0, it's not working

        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { () -> Void in

            self.newAddress = self.getReversedGeocodeLocation()

            dispatch_semaphore_signal(semaphore)
        }

        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

        //dispatch_semaphore_signal(semaphore)

        print(self.newAddress + ".")

        self.performSegueWithIdentifier("mainToNewAddress", sender: self)

func getReversedGeocodeLocation() -> String{

    var address = ""
    CLGeocoder().reverseGeocodeLocation(self.newAddressLocation, completionHandler: {(placemarks, error) -> Void in

        if error != nil {
            print("Reverse geocoder failed with error" + error!.localizedDescription)
            return
        }
        else if placemarks?.count > 0 {
            let pm = placemarks?.first!
            address = pm!.name!
        }
        else {
            print("Problem with the data received from geocoder")
        }
    })

    return address
}

Using a semaphore and dispatching the call to getReversedGeocodeLocation is unnecessarily complicated. CLGeocoder().reverseGeocodeLocation is already asynchronous. If you simply pass a completion handler closure to getReversedGeocodeLocation then you can use that to invoke the segue;

self.getReversedGeocodeLocation(self.newAddressLocation, completionHandler: { (address,error) in 
    guard error == nil else {
        print("Reverse geocoder failed with error" + error!.localizedDescription)
        return
    }

    guard let address = address else {
        print("No address returned")
        return
    }

    self.newAddress = address
    dispatch_async(dispatch_get_main_queue(), {
        self.performSegueWithIdentifier("mainToNewAddress", sender: self)
    })
})

func getReversedGeocodeLocation(addressLocation: String, completionHandler:((address: String?, error: NSError?) -> Void))) {
    CLGeocoder().reverseGeocodeLocation(self.newAddressLocation, completionHandler: {(placemarks, error) -> Void in

        var address = nil

        if placeMarks.count > 0 {
            if let pm = placeMarks!.first {
                address  = pm.name
            }
        } else {
            print("Problem with the data received from geocoder")
        }

        completionHandler(address,error)
    })
}

I dont think its a problem with the semaphore, its with your getReversedGeocodeLocation function. the CLGeocoder().reverseGeocodeLocation is an async function, therefore the return statement is going to execute before the completion block of CLGeocoder().reverseGeocodeLocation is executed, which is why self.newAddress is being assigned an empty string.

To fix this you could use another semaphore inside the getReversedGeocodeLocation to block until the completion handler is finished, or use a delegate to notify something that the completion handler is finished (which is usually the right way to do it).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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