In my main starting thread I need to pause the code and start a new thread and wait until I get user input. Then Id like to discard the new thread made and go back to where the main thread left off. But whats happening is that the new thread is called but the main thread keeps going with the code. How do I deal with this without interfering with the user being able to use the interface buttons? I think that maybe another nscondition needs to be made in my if(moveCount == 2) statement? Or that my main thread needs to wait for a signal from my other thread notifying user input is received.
ADDITIONAL NOTE: I would also like the original thread paused in such a way that I can still use my 2 UIButtons in my interface.
MORE ADDITIONAL NOTES AS TO THE QUESTIONS IM RECEIVING: This is a game I'm making. Somewhere mid code means in my main thread in one of my methods. This method decides which direction I'm moving in then i get a tag difference, then make an attack. But sometimes 2 attacks can be made so its at this point the user has 2 buttons to click on the screen to decide which attack to make.
Also its quiet clear to me that I should not pause the main thread now. If so what is the alternative? Thanks
NSCondition and NSThread
#property (strong, nonatomic) NSCondition *condition;
#property (strong, nonatomic) NSThread *aThread;
In my viewDidLoad create the following.
// create the NSCondition instance
self.condition = [[NSCondition alloc]init];
// create the thread
self.aThread = [[NSThread alloc] initWithTarget:self selector:#selector(threadLoop) object:nil];
somewhere mid code..
if(moveCount == 2){
[self.aThread start];
}
// I need to get captureDecision from user from 2 buttons before continue.. How do I pause this thread when aThread is started. Then when capture decision is received discard aThread and start here.
NSLog(#"Capture Decision = %d", captureDecision);
if(captureDecision == 1){
tagDifference = newButton.tag - currentButton.tag;
}else{
tagDifference = currentButton.tag - newButton.tag;
}
}
aThread Method
-(void)threadLoop{
NSLog(#"Thread Loop Triggered");
while([[NSThread currentThread] isCancelled] == NO)
{
[self.condition lock];
while(captureDecision == 0)
{
[self.condition wait];
}
[self.condition unlock];
}
[NSThread exit]; //exit this thread when user input is received
}
What you are requesting is not possible, the main thread IS the one that handles user interactions, whenever you "halt" the main thread the interface becomes unresponsive.
Based on apple's documentation:
The main run loop of your app is responsible for processing all
user-related events. The UIApplication object sets up the main run
loop at launch time and uses it to process events and handle updates
to view-based interfaces. As the name suggests, the main run loop
executes on the app’s main thread. This behavior ensures that
user-related events are processed serially in the order in which they
were received.
Which means that if you halt the main thread you won't be able to.
https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/ManagingYourApplicationsFlow/ManagingYourApplicationsFlow.html#//apple_ref/doc/uid/TP40007072-CH4-SW14
(Unless there is some strange way to make a background thread handle the user interactions which i might not be aware of, but still its pretty strange)
As others have told you here, your approach is far from optimal. Questions you have to ask yourself are, in what part of the code do you want to wait for a certain event to occur, and for what purpose.
If you give us a more detailed explanation of what you want to achieve then we can offer you a much better solution.
EDIT:
What kind of game is it? turn based? something that gets drawn constantly like with a timer. Basically you have to use functions, for example, if it was a card game, you would lay all the cards and wait for user input, when the user hits the button you would run "calculate outcome function" and make the proper changes in the board and wait for the next input.
If you want to pause the thread i would assume it is because something is running constantly, there is a special timer that syncs with the screen called CADisplayLink
https://developer.apple.com/LIBRARY/IOS/documentation/QuartzCore/Reference/CADisplayLink_ClassRef/Reference/Reference.html
You can even "pause" this timer to wait for an input from the user.
BTW, if you DONT want to let the user interact with the interface until an event occurs all you have to do is, self.view.userInteractionEnabled = NO; this disables all interactions on the screen until you set it back to "YES".
EDIT 2:
The way you change the flow varies on what you are using to make your game, OpenGL? Quartz2D? simple views?
It kinda feels you are a bit lost, you might wanna start from here:
http://www.raywenderlich.com/tutorials
(Scroll down to the making games part)
It looks like you are going about this wrong. By design the main thread should not be blocked. You should use some sort of callback/delegate from your background thread to your main thread to set your tag decision. I'd provide a bit more info/examples, but I'm not really sure what you trying to achieve or where your "somewhere mid code" section takes place. I thought the main thread, but then you call a selector to be performed on the main thread.
Related
Consider the following case:
Main Thread -----UIView info \ --------------------------------------- / Button Updated ------
\ (Some Event) / (Some Event)
\ /
BG Thread ---------------------Validate UIView info-----Update Button------------------------
On the main thread, a UIView is present
UIView makes a dispatch call is made to a background thread
In the BG Thread, the UIView's info is validated.
BG Thread makes a dispatch call to a UIButton on the main thread to update it.
My question is - notice how there is a gap between UIView info and the UIButton which means that the app could technically be updated during this time. How can I possible stop this gap? Essentially, from the BG thread, block the Main Thread till the call comes back?
You cannot and must never block the main thread. If you do, the interface will freeze and, if the blockage lasts too long, the WatchDog process will kill your app dead before the user's very eyes.
You can give the user a sense that something is going on, and discourage the user from doing anything, by, say, turning off user interaction and putting up a spinner. But in general, yes, multithreading is hard; you must be prepared for the possibility that you will come back onto the main thread when the app has changed state.
Rather than block a main thread, disable user input controls in your view until the validation is complete, then re-enable them.
It would also make sense to add an activity indicator with hidesWhenStopped set to true; it will show the user that there's background work in progress if you start it when the background work starts, and stop it when validation is complete.
If there's ever a chance the background process could hang or take longer, e.g. if it's making a network request, you might show/enable a cancel button and a way to terminate it.
Showing activity indicator and possibly providing a cancel button both require that the main thread keep running, so definitely don't block it!
Your button should not be updated in the background. You should always modify UIKit components on the main thread.
You should also never block the main thread, what you're looking for is the show the user an indication that a background process is active. 'UIActivityIndicatorView' might be a good thing to show the user, you could also disable user interaction on the view to prevent the user from touching anything if it's critical for them to wait until the operation is complete but not recommended.
Yes, you should never block the main thread and update UI only on main thread.
That said - show a spinner / activity indicator while busy on the background.
Think carefully about the UI and e.g. present something so the user can not change something while you are busy with e.g. dialog or popover or something like that.
In practise this often becomes more a question of UX than blocking.
We all show activity indicator while some lengthy operation is happening in background. Though the activity indicator shows a constantly rotating wheel it won't burden the main thread, because other UIComponents in the same screen still react to the touches.
What I think I know:
I know all touch events are handled by main thread, and main Queue is being used to queue the events. Considering main queue is Serialized queue and only one task at a time can run at any given point in time, alley touch events should get queued up in main queue, while my main thread is busy in refreshing the screen/calling drawrect of UIActivityIndicator.
Study:
I have looked into the code of third party activity indicators. Most of them use CABasicAnimation and call repeat always on animation. While few work directly use NSTimer to repeatedly call drawrect with a small delay. Their code works because there is a small delay in calling drawrect and the method drawrect in itself is light weight.
None of it won't take the loads off the main thread but rather they carefully place load on main thread enough to keep their animation going yet keeping main thread free to handle touch events
What I want to know:
1 - Is this strategy to implement activity indicator is correct? or statement like this
self.timer =[NSTimer timerWithTimeInterval:0.1 target:self
selector:#selector(setNeedsDisplay) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
in one of the third party activity indicator that I saw has any special effect?
2 - If I run CABasicAnimation/transaction and repeat the animation forever will it have any special effects on the load of main thread compared to repeatedly calling setNeedsDispaly/drawrect manually?
I'm not sure whether it will help to implement your own activity indicator, but the system one UIActivitiyIndicatorView is just a UIImageView with an array of 12 images that replace each other over time.
Apple made a pretty neat trick by making their spinner discrete. It allowed them to have a simple implementation that doesn't create any computational load on CPU.
UPD
Returning to the things you want to know:
1 - It's not, because implementing manual frame drawing in drawRect is fully done by CPU. And 2 - I can't say for sure, but if one believes what Apple says in documentation and videos about Core Animation it is heavily optimised and runs on Metal or at least OpenGL underneath, so leverages power of GPU.
I have an iOS application that has an NSTimer which fires every 5 seconds. This then posts a notification, telling several controllers that they must now recalculate some data and then update their UI.
When this all happens on the main thread, scrollviews can become jittery as the data is processed.
Using GCD, I have wrapped the code called when a notification is posted:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Code here
dispatch_async(dispatch_get_main_queue(), ^(){
// Reload tableviews and UI
};
};
Unfortunately, this leads in certain cases to several threads accessing the same instance variables. This causes my application to crash as sometimes it ends up mutating an array being enumerated in another thread.
Wrapping the code in a synchronised block prevents this.
When the user scrolls the tableview, several methods are called to calculate the height etc, on the main thread. The code within the background thread is working on the code needed by the main thread. As such, this can usually further cause crashes
I have tried setting the variables to 'atomic', but this doesn't solve the issue. Ideally, I don't want to have the main thread waiting on the background thread, but I am not sure how to best resolve this issue given that they need the same data?
This is pretty classic multithreaded programming issues. There are a number of ways to solve it with basic locks (#synchronized blocks), reader/writer locks, etc but the problem is often that you can't control when the user is going to scroll or take other action. If you #synchronize, you have to do it anywhere that data is touched, including your UITableView data source methods. That can lead to stuttering if the background processing happens to be in the middle of something.**
Personally, I would use an immutable snapshot mechanism.
Have the background thread produce the results, then include just the data the UI needs to display in the notification data as an immutable snapshot (copy). That way the background thread never modifies the data the UI is currently reading for display. How you would implement this is highly dependent on how much data you are talking about and the form it takes, but the safe way would be to have copies of your classes with readonly properties. Alternatively, you can use a "frozen" flag. Make a copy, then set frozen = YES on the copy. The UI thread will only ever see "frozen" or readonly objects coming from the background thread.
The benefit is the UI never causes the background thread to stall and there are no locks required. The downside is increased memory usage, though if the amount of data is large you can use copy-on-write mechanisms to allow the background thread and UI thread to share the data, even though logically the UI thread has a completely separate copy.
** Note: In most applications, you don't have this sort of continuous background processing going on so those apps can use simpler mechanisms. This is typically a form of message passing where the background thread finishes its task and "passes the message" to the UI thread (passes the results). At that point the background work is finished so there is no concurrent modification happening.
I found interesting things.. Following code doesn't show #"One" and it show #"Two" after 3 seconds delay..
I think that #"One" need to be shown and then 3 seconds delay and then #"Two" need to pop up..
Am I wrong?
self.statusLabel.text = #"One";
sleep(3);
self.statusLabel.text = #"Two";
Thanks..
If you're doing this on the main thread, that sleep(3) will block it, freezing the app for 3 seconds. Event processing, including things like repainting the UI, won't happen til that's over.
To get what you're expecting, try something like this:
[self.statusLabel setText:#"One"];
[self.statusLabel performSelector:#selector(setText:)
withObject:#"Two"
afterDelay:3.0];
Does the first change, then queues up an invocation performing the second change to happen in the future. Then returns control to the OS to do any necessary redrawing.
Your notion of how things should work is incorrect.
self.statusLabel.text = #"One";
This sets the value of the statusLabel field to "One". This does not immediately draw to the screen. Instead, the label will mark itself as needing display. At the end of the current run loop cycle, all the views marked as needed display will be drawn, and then their contents flushed to the screen.
Next you do:
sleep(3);
self.statusLabel.text = #"Two";
This blocks the main thread for 3 seconds (never returning to the run loop to do the work mentioned above), then changes the value to "Two" which marks the view again as needing display.
When it is eventually drawn, and flushed to the screen, the current value is "Two".
It is hard to give more specific advice about what you should be doing, because it isn't clear if there is a real problem you are trying to solve, or just experimenting in order to learn more about the frameworks.
But you should almost never use sleep, and you certainly shouldn't be blocking the main thread with sleep for several seconds.
You're wrong...
Think of it this way: when you execute code in a block you're telling iOS what you want to do. It only actually implements your instructions after you pass control back to the OS.
Your code blocks the main thread (which is a very bad thing to do).
What you need to do is set the label to "One" then set a timer that will fire in three seconds time. The code in the timer would set the text of the label to "Two."
in my iPhone app, I have used 2 threads. Both functions differently in different class. On particular condition I want to pause particular thread so I have used
[NSThread sleepForTimeInterval:]
So my question is am I doing right or this sleep causes sleep in whole application? If yes what is other alternative?
If you want to pause your thread for a given amount of time, then [NSThread sleepForTimeInterval:] is just fine. However if you want to wait for a given event to occur (e.g. wait for another thread to catch up) you should take a look at the NSCondition class.
Yeah this is absolutely right. This will pause the current thread on which you are calling [NSThread sleepForTimeInterval:]