Reputation: 1332
I am trying to load a set of buttons that will slide in one after the other when the menu is loaded. So I did this:
buttonTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(MainMenu.enterButtons), userInfo: nil, repeats: true)
buttonTimer.fire()
That would run the enterButtons() method, which will run the move action for buttons. This timer is created during the initializer of this menu because I want the buttons to load in on init. However, during my initializer I also initialize a class which is performance heavy, since it holds all my levels:
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.initialLevel.levelHolder = LevelHolder(screenWidth: Float(self.initialLevel.size.width), scene: self.initialLevel, screenSize: self.initialLevel.frame, initialLevel: (1,1))
}
So I did this in async because if I did not it would hold up all the other functions of the initializer. However, I think those two threads are actually just one. The order of the async tasks are:
I was hoping that these two task would run simultaneously. However, it seems that instead of the three threads I was hoping for (the main thread, the one for the level holder, and the load buttons) I believe there is only two: the main thread and one for the two async tasks I created. During my debug session, I noticed that the load buttons task waits for the level holder to be initialized. However, I want to create three separate async threads. How can I do this?
Edit
I an attempt to achieve concurrency for the level holder I tried this, suggested by @UpholderOfTruth:
DispatchQueue.global(qos: .background) {
self.initialLevel.levelHolder = LevelHolder(screenWidth: Float(self.initialLevel.size.width), scene: self.initialLevel, screenSize: self.initialLevel.frame, initialLevel: (1,1))
}
But I get an error that says:
LevelHolder is not convertible to 'LevelHolder!'
because self.initialLevel.levelHolder is of type LevelHolder! but I am setting it equal to LevelHolder.
How can I avoid this error? I tried doing this:
self.initialLevel.levelHolder = LevelHolder(screenWidth: Float(self.initialLevel.size.width), scene: self.initialLevel, screenSize: self.initialLevel.frame, initialLevel: (1,1))!
But then Xcode suggests warns I
cannot force unwrap to a non-optional type 'LevelHolder'
initialLevel is initialized within the method that initializes level holder:
func initializeLevelHolder() {
initialLevel = InitialLevel()
initialLevel.size = self.size
DispatchQueue.main.asyncAfter(deadline: .now()) {
self.initialLevel.levelHolder = LevelHolder(screenWidth: Float(self.initialLevel.size.width), scene: self.initialLevel, screenSize: self.initialLevel.frame, initialLevel: (1,1))
}
}
It is a instance variable of this menu defined as
var initialLevel : InitialLevel!
Upvotes: 3
Views: 1731
Reputation: 4711
You are pushing your levelHolder code back onto the main thread immediately and setting the button time to fire after 0.1 seconds and then firing it manually which happens after the method finishes. So everything is happening on the main thread and your levelHolder processing holds up the button processing.
You can put the levelHolder processing onto a background thread like this:
DispatchQueue.global(qos: .background).async {
self.initialLevel.levelHolder = LevelHolder(screenWidth: Float(self.initialLevel.size.width), scene: self.initialLevel, screenSize: self.initialLevel.frame, initialLevel: (1,1))
}
However don't forget any updates to the UI still need to go back on the main thread like this:
DispatchQueue.main.async {
// Update the UI
}
Unfortunately there is nothing that can be done about that as all UI updates have to happen on the main thread so will block each other to some degree.
Upvotes: 1