iOS/Swift中如何等待一个函数结束后再开始第二个函数

17

我基本上有两种方法,在我的viewDidLoad中被调用。第一个方法获取用户的搜索偏好并将偏好保存到顶部的变量中。之后,我想在第二个函数中访问和处理这些变量。但是现在当我想要在第二个函数中访问它们时,这些变量总是为空。

我需要如何调整我的viewDidLoad,以便第二个函数仅被执行一次且我的数据请求已成功执行?

var searchLocation = String()
var searchLocationCoordinates = [String:Double]()
var searchRange = Int()


override func viewDidLoad() {
    super.viewDidLoad()

    // Gets the user's search preference
    getTheSearchLocationAndRange()

    // Loads the data from the database
    loadDataFromDatabase()
}

到目前为止,我已经用dispatch_asynch或完成处理程序阅读了一些内容。也许有人可以发布一些代码,我可以在我的viewDidLoad中使用它并使其工作吗?


5
你并没有明确表示,但getThSearchLocationAndRange函数必须是异步的?如果是这样的话,你就不能等待它完成,否则你的GUI会停止响应。相反,你可以将第一个函数作为参数传入完成块,并将第二个函数放在块体中,这是一种方法,也有其他方式可以实现。 - Gruntcakes
好的,我明白了!你有一些示例代码可以展示给我吗?但这也是我在其他帖子中看到的一个使用完成处理程序的想法! - makle
getTheSearchLocationAndRange函数中是否有一个调度函数? - FredericP
嗨FredericP,目前还没有这样的东西。 - makle
@Mattk90 你尝试过我在答案中发布的代码吗? - Coder1000
3个回答

22

您可以使用 Swift 闭包!它们就是为此而设计的。

请参考苹果指南:Closures

以下是适用于您特定情况的代码。

FinishedDownload 是一个闭包。当调用 getTheSearchLocationAndRange() 时,其代码被执行,直到 completed() 行等待函数的所有进程完成。一旦进程完成(例如下载),completed() 调用闭包,激活在 getTheSearchLocationAndRange { () -> () in 中定义的代码。因此,只有在完全执行 getTheSearchLocationAndRange() 并数据存在(非 nil)时才会调用 loadDataFromDatabase()

    var searchLocation = String()
    var searchLocationCoordinates = [String:Double]()
    var searchRange = Int()
    typealias FinishedDownload = () -> ()

    override func viewDidLoad() {
        super.viewDidLoad()

        getTheSearchLocationAndRange()
    }

    func getTheSearchLocationAndRange(completed: FinishedDownload) {

           // Code for searching Location Range HERE

           completed()
    }

    getTheSearchLocationAndRange { () -> () in
        loadDataFromDatabase()
    }

我希望这解决了您的问题并回答了您的问题 :)

顺便说一下,关于“让GUI挂起”的部分,Alamofire会自动为您处理。 如果您不使用Alamofire,则必须手动将异步请求分配给后台线程,以使您的GUI不会变得无响应。


2
如果我有需要返回的 getTheSearchLocationAndRange 参数,该怎么办? - Sam

3
我写了一个演示项目并将其发布在GitHub上,模拟处理异步网络下载。请查看DuncanMC/SwiftCompletionHandlers
具体来说,请查看asyncFetchImage()方法,它几乎完全做到了这个线程所讨论的内容:内部使用异步方法,并采用完成块(completion block)一旦异步加载完成就调用它。
这是您应该使用的一般模式。编写一个带有完成块/闭包(completion block/closure)的方法。在内部,让该方法调用它需要的任何异步函数,然后从异步方法调用的完成闭包中调用您的完成闭包。
函数asyncFetchImage如下所示:
func asyncFetchImage(imageName imageName: String,
  completion: (
    image: UIImage?,
    status: String) -> ())
{
  print("Entering \(#function)")

  //Simulate a network operation by waiting a few seconds before loading an image
  let nSecDispatchTime = dispatch_time(DISPATCH_TIME_NOW, Int64(3.0 * Double(NSEC_PER_SEC)))
  let queue = dispatch_get_main_queue()
  dispatch_after(nSecDispatchTime, queue)
    {
      () -> Void in
      let result = UIImage(named: imageName)
      print("Loading image in background")
      let status = result != nil ? "image loaded" : "Error loading image"
      print("About to call completion handler")
      completion(image: result, status: status)
  }
  print("Leaving \(#function)")
}

2

好的,我找到了一个解决方案。我基本上在第一个函数的末尾调用了该函数。

因此,基本上是这样的:

    var searchLocation = String()
    var searchLocationCoordinates = [String:Double]()
    var searchRange = Int()


    override func viewDidLoad() {
        super.viewDidLoad()

        // Gets the user's search preference
        getTheSearchLocationAndRange()
    }

    func getTheSearchLocationAndRange() {
    // Code for getTheSearchLocationAndRange()

    loadDataFromDatabase()
    }

    func loadDataFromDatabase(){
    // Code for loadDataFromDatabase()
    }

2
这个问题在于它使得 getTheSearchLocationAndRange() 函数变得不太有用,除非它总是要调用 loadDataFromDatabase。如果你想让这个方法更加灵活,可以使用完成块方法。 - mginn
好的,我同意这一点。在我的情况下,我永远不需要独立使用这两个函数。因此,我想我可以这样解决它。但是我要承认,你的解决方案可能在编程上更好!哈哈 - makle
3
如果在“getTheSearchLocationAndRange()”中的任一过程需要更长时间,那么此操作可能会失败。这根本不是解决方案。 - Hope
这不是除了你特定的情况之外的任何问题的解决方案。它可能对你有效,但原因是错误的。如果在父函数中有异步功能,则子函数仍将被调用而不等待其他代码完成。 - eResourcesInc

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接