【问题标题】:Swift semaphore not waiting for function to finish before calling UISwift 信号量在调用 UI 之前不等待函数完成
【发布时间】:2016-05-25 02:20:53
【问题描述】:

我正在尝试延迟转场,直到我收到来自 reverseGeocodeLocation 电话的回复。但是,当使用断点检查值何时实际更改时,它仍然会在 UI 转换发生后发生。我尝试过让函数为 void 并使用当前的 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
}

【问题讨论】:

  • (1) 您应该使用 count = 0 创建信号量。(2) 可能是您的 getReversedGeocodeLocation() 本身是异步的?
  • 使用信号量是不必要的复杂。只需将完成闭包传递给getReversedGeocodeLocation 并从reverseGeocodeLocation 的完成处理程序调用该闭包;让闭包执行转场
  • 您在上述函数中的信号量是无用的。如果您要使用信号量“等待”,为什么还要分派异步。您可以使用reverseGeocodeLocation 上的信号量来使其同步。
  • 是的,一旦你让我回顾我最初使用它的原因,我就意识到这毫无意义。我编辑了我的代码,它仍然无法正常工作,但我希望它朝着正确的方向发展
  • @brandon 不确定这是否是一个可能的问题,但是当我的 CLGeocoder().reverseGeocodeLocationdispatch_semaphore_wait 有断点时,前者在线程 10 中,后者在线程 1 中

标签: ios swift mapkit semaphore


【解决方案1】:

使用信号量并将调用分派到getReversedGeocodeLocation 是不必要的复杂。 CLGeocoder().reverseGeocodeLocation 已经是异步的。如果您只是将完成处理程序闭包传递给getReversedGeocodeLocation,那么您可以使用它来调用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)
    })
}

【讨论】:

  • 我得到guard let address = address else 的“条件绑定的初始化程序必须具有可选类型”编辑:没关系,在 completionHandler 中找到它
  • 首先谢谢你,它有效。但是,我至少想知道为什么我的代码不起作用。我是否需要简单地将self.performSegueWithIdentifier 放在dispatch_async 中并且仍然具有信号量框架?或者如果可以的话,我应该一直这样做吗?
  • 最好避免使用信号量。完成处理程序方法在 iOS 世界中非常普遍
  • 但是要回答你的问题,`dispatch_semaphore_signal(semaphore)` 需要放在你调用完成处理程序的地方。然而,这种方法的问题是,dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER) 将阻塞主线程并使 UI 无响应
  • 好的,谢谢,在开发应用程序时,我正在努力学习最好的“iOS 世界”实践,以便更好地了解代码的质量。
【解决方案2】:

我不认为信号量有问题,它与您的getReversedGeocodeLocation 函数有关。 CLGeocoder().reverseGeocodeLocation 是一个异步函数,因此 return 语句将在 CLGeocoder().reverseGeocodeLocation 的完成块执行之前执行,这就是为什么 self.newAddress 被分配一个空字符串。

要解决此问题,您可以使用 getReversedGeocodeLocation 中的另一个信号量来阻止直到完成处理程序完成,或者使用委托通知完成处理程序完成的某些内容(这通常是正确的方法)。

【讨论】:

  • 我的信号量应该在异步块内还是在它附近?
  • 将整个dispatch_async块放在reverseGeocodeLocation中,并没有产生不同的变化。
  • 您需要在 CLGeocoder().reverseGeocodeLocation 完成块内完成信号量信号。
  • getReversedGeocodeLocation 的开头放置一个新的dispatch_semaphore_create,在CLGeocoder().reverseGeocodeLocation 的末尾放置一个信号,并在返回语句之前放置一个等待,这应该就是你所需要的
  • completionHandler(placemarks!.first!.name! ?? "", error) dispatch_semaphore_signal(semaphore)我后来放了信号,现在它一直在运行
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-12-18
  • 1970-01-01
  • 2013-05-09
  • 1970-01-01
  • 2017-08-19
相关资源
最近更新 更多