Семафор Swift не ждет завершения функции перед вызовом пользовательского интерфейса

Я пытаюсь отложить переход до тех пор, пока не получу ответ от reverseGeocodeLocation звонка. Однако при использовании точек останова для проверки фактического изменения значения это все равно происходит после перехода пользовательского интерфейса. Я пробовал сделать функцию недействительной и с текущим возвратом String.

РЕДАКТИРОВАНИЕ КОДА:

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

СТАРЫЙ КОД:

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
}

person Community    schedule 25.05.2016    source источник
comment
(1) Вы должны создать семафор с count = 0. (2) Может быть, ваш getReversedGeocodeLocation() сам является асинхронным?   -  person Code Different    schedule 25.05.2016
comment
Использование семафора излишне сложно. Просто передайте завершение в getReversedGeocodeLocation и вызовите это закрытие из обработчика завершения reverseGeocodeLocation; заставить закрытие выполнить переход   -  person Paulw11    schedule 25.05.2016
comment
Ваш семафор в приведенной выше функции бесполезен. Зачем беспокоиться об отправке async, если вы собираетесь использовать семафор для ожидания. Вместо этого вы можете использовать семафор: reverseGeocodeLocation, чтобы сделать его синхронным.   -  person Brandon    schedule 25.05.2016
comment
Да, я понял, что это бессмысленно, когда ты заставил меня оглянуться назад, почему я вообще его использовал. Я отредактировал свой код, он все еще не работает, но надеюсь, что он в правильном направлении   -  person    schedule 25.05.2016
comment
@brandon не уверен, что это возможная проблема, но когда у меня есть точки останова для моего CLGeocoder().reverseGeocodeLocation и для dispatch_semaphore_wait, первый находится в потоке 10, а второй - в потоке 1   -  person    schedule 25.05.2016
comment
Я бы просто использовал обработчик завершения и отправил его в основную очередь, чтобы продвинуть ваш переход: ideone.com/vWAfu7   -  person Brandon    schedule 25.05.2016


Ответы (2)


Использование семафора и отправка вызова getReversedGeocodeLocation излишне сложны. CLGeocoder().reverseGeocodeLocation уже асинхронный. Если вы просто передаете закрытие обработчика завершения в getReversedGeocodeLocation, вы можете использовать его для вызова перехода;

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)
    })
}
person Paulw11    schedule 25.05.2016
comment
Я получаю Инициализатор для условной привязки, должен иметь необязательный тип для guard let address = address else EDIT: nevermind, нашел его в завершенииHandler - person ; 25.05.2016
comment
в первую очередь спасибо, работает. Однако я хотел бы хотя бы знать, почему мой код не работает. Мне нужно было просто поместить self.performSegueWithIdentifier в dispatch_async и все еще иметь структуру семафоров? Или я всегда должен делать это, если могу? - person ; 25.05.2016
comment
Лучше всего избегать семафоров. Подход обработчика завершения чрезвычайно распространен в мире iOS. - person Paulw11; 25.05.2016
comment
Но чтобы ответить на ваш вопрос, "dispatch_semaphore_signal (семафор)" должен идти туда, где вы вызываете обработчик завершения. Однако проблема с этим подходом заключается в том, что dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) заблокирует основной поток и сделает пользовательский интерфейс невосприимчивым. - person Paulw11; 25.05.2016
comment
Хорошо, спасибо, работая над приложением, я пытаюсь изучить лучшие мировые практики iOS, чтобы лучше понимать качество кода. - person ; 25.05.2016

Я не думаю, что это проблема с семафором, это с вашей функцией getReversedGeocodeLocation. CLGeocoder().reverseGeocodeLocation является асинхронной функцией, поэтому оператор return будет выполняться до того, как будет выполнен блок завершения CLGeocoder().reverseGeocodeLocation, поэтому self.newAddress назначается пустая строка.

Чтобы исправить это, вы можете использовать другой семафор внутри getReversedGeocodeLocation для блокировки до тех пор, пока обработчик завершения не будет завершен, или использовать делегат, чтобы уведомить что-то о завершении обработчика завершения (что обычно является правильным способом сделать это).

person Fonix    schedule 25.05.2016
comment
мой семафор должен быть внутри асинхронного блока или вокруг него? - person ; 25.05.2016
comment
Поместите весь блок dispatch_async в reverseGeocodeLocation, и он не приведет к различным изменениям. - person ; 25.05.2016
comment
Вам нужно, чтобы сигнал семафора выполнялся ВНУТРИ блока завершения CLGeocoder (). ReverseGeocodeLocation. - person ghostatron; 25.05.2016
comment
поместите новый dispatch_semaphore_create в начало getReversedGeocodeLocation, поместите сигнал в конец CLGeocoder().reverseGeocodeLocation и поставьте ожидание непосредственно перед оператором return, и это должно быть все, что вам нужно - person Fonix; 25.05.2016
comment
completionHandler(placemarks!.first!.name! ?? "", error) dispatch_semaphore_signal(semaphore) Я поставил сигнал потом и теперь он работает вечно - person ; 25.05.2016