简体   繁体   中英

Swift Closures and order of execution

I'm struggling a little bit trying to create an application for my own education purposes using Swift.

Right now I have the following (desired) order of execution:

  1. TabView
  2. FirstViewController - TableView
  3. Check into CoreData
    • If data exists update an array using a closure
    • If data doesn't exists then download it using Alamofire from API and store it into Core Data
  4. SecondViewController - CollectionView
    • Checks if data of images exists in Core Data, if it does, loads it from there, otherwise download it.

The problem that I'm struggling the most is to know if the code after a closure is executed after (synchronously) the closure ends or it might be executed before or while the closure is executed.

For example:

FirstViewController

var response: [DDGCharacter]
//coreData is an instance of such class
coreData.load(onFinish: { response in //Custom method in another class
    print("Finished loading")
    self.response = response
})

print("Executed after loading data from Core Data")
//If no data is saved, download from API
if response.count == 0 {
    //Download from API
}

I have done the above test with the same result in 10 runs getting:

Finished loading
Executed after loading data from Core Data

In all 10, but it might be because of load is not taking too much time to complete and thus, appear to be synchronous while it's not.

So my question is, is it going to be executed in that order always independent of amount of data? Or it might change? I've done some debugging as well and both of them are executed on the main thread as well. I just want to be sure that my suppositions are correct.

As requested in the comments, here's the implementation done in the load() method:

func load(onFinish: ([DDGCharacter]) -> ()) {
    var characters: [DDGCharacter] = []

    guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
        return
    }

    let managedContext = appDelegate.persistentContainer.viewContext

    let fetchRequest = NSFetchRequest<NSManagedObject> (entityName: "DDGCharacter")

    do {
        characters = try managedContext.fetch(fetchRequest) as! [DDGCharacter]
    } catch let error as NSError {
        print("Could not fetch. \(error), \(error.userInfo)")
    }

    onFinish(characters)
}

Your implementation of load(onFinish:) is very surprising and over-complicated. Luckily, though, that helps demonstrate the point you were asking about.

A closure is executed when something calls it. So in your case, onFinish is called at the end of the method, which makes it synchronous. Nothing about being "a closure" makes anything asynchronous. It's just the same as calling a function. It is completely normal to call a closure multiple times ( map does this for instance). Or it might never be called. Or it might be called asynchronously. Fundamentally, it's just like passing a function.

When I say "it's slightly different than an anonymous function," I'm just referring to the "close" part of "closure." A closure "closes over" the current environment. That means it captures variables in the local scope that are referenced inside the closure. This is slightly different than a function (though it's more about syntax than anything really deep; functions can actually become closures in some cases).

The better implementation would just return the array in this case.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM