在开始第二个函数之前,如何等待一个函数在 iOS/Swift 上结束

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36829749/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-31 08:57:02  来源:igfitidea点击:

How to wait for a function to end on iOS/Swift, before starting the second one

iosswift

提问by Mattk90

I basically have to methods, which are being called in my viewDidLoad. The first one gets the user's search preferences and saves the preferences to variables at the top. After that I want to access and work with these variables in the second function. But now the variables are always nil when I want to access them in the second function.

我基本上必须使用在我的 viewDidLoad 中调用的方法。第一个获取用户的搜索首选项并将首选项保存到顶部的变量中。之后,我想在第二个函数中访问和使用这些变量。但是现在当我想在第二个函数中访问它们时,变量总是为零。

How do I need to adjust my viewDidLoad, so the second function is only executed once and my data request has been performed successfully?

我需要如何调整我的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()
}

I have read stuff so far with dispatch_asynch or completion handler. Maybe someone can post some code which I can use in my viewDidLoad and makes it work?

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

采纳答案by Mattk90

Okay I've found an solution. I basically called the function at the end of the first one.

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

So basically:

所以基本上:

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

回答by Coder1000

You can use Swift closures! They are made for that.

你可以使用 Swift 闭包!它们就是为此而生的。

Please refer to the Apple guide: Closures

请参阅 Apple 指南:Closures

Here's the code you need in your particular case.

这是您在特定情况下所需的代码。

FinishedDownload is the closure. When getTheSearchLocationAndRange()is called, its code is executed until the completed()line which waits for all processes of the function to finish. Once the processes finish (downloads for example), completed()calls the closure which activates the code defined in getTheSearchLocationAndRange { () -> () in. Therefore, loadDataFromDatabase()is only called once getTheSearchLocationAndRange()has entirely finished executing and the data is present (not nil).

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

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

I hope this solved your issue and answered your question :)

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

BTW, about the "leaving your GUI hanging" part, Alamofire takes automatically care of this for you. If you don't use Alamofire, then you will have to manually assign the asynchronous request to a background thread so your GUI doesn't become unresponsive.

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

回答by Duncan C

I wrote a demo project and posted it on GitHub that simulates handling an asynchronous network download. Take a look at DuncanMC/SwiftCompletionHandlers.

我写了一个演示项目并将其发布在 GitHub 上,模拟处理异步网络下载。看看DuncanMC/SwiftCompletionHandlers

Specifically look at the method asyncFetchImage(), which does almost exactly what this thread is talking about: Uses an asynchronous method internally, and takes a completion block that it calls once the asynchronous load is done.

具体看一下 asyncFetchImage() 方法,它几乎完全符合该线程所说的内容:在内部使用异步方法,并在异步加载完成后调用一个完成块。

That is the general pattern you should use. Write a method that takes a completion block/closure. Internally, have that method call whatever asynchronous function it needs and then call your completion closure from inside the asynchronous method call's completion closure.

这是您应该使用的一般模式。编写一个接受完成块/闭包的方法。在内部,让该方法调用它需要的任何异步函数,然后从异步方法调用的完成闭包内部调用您的完成闭包。

The function asyncFetchImagelooks like this:

该函数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)")
}