如何在启动第二个函数之前等待函数在iOS / Swift上结束

时间:2020-12-12 20:56:08

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中使用的代码,并使其工作?

3 个解决方案

#1


7  

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

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

Please refer to the Apple guide: Closures

请参阅Apple指南:闭包

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 {() - >()中定义的代码。因此,只有getTheSearchLocationAndRange()完全执行完成后才调用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()
    }

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不会无响应。

#2


3  

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 asyncFetchImage looks 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)")
}

#3


1  

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

#1


7  

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

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

Please refer to the Apple guide: Closures

请参阅Apple指南:闭包

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 {() - >()中定义的代码。因此,只有getTheSearchLocationAndRange()完全执行完成后才调用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()
    }

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不会无响应。

#2


3  

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 asyncFetchImage looks 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)")
}

#3


1  

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