I have method which makes UI changes in some cases.
For example:
-(void) myMethod {
if(someExpressionIsTrue) {
// make some UI changes
// ...
// show actionSheet for example
}
}
Sometimes myMethod is called from the mainThread sometimes from some other thread.
Thats is why I want these UI changes to be performed surely in the mainThread.
I changed needed part of myMethod this way:
if(someExpressionIsTrue) {
dispatch_async(dispatch_get_main_queue(), ^{
// make some UI changes
// ...
// show actionSheet for example
});
}
So the questions:
Is it safe and good solution to call dispatch_async(dispatch_get_main_queue() in main thread? Does it influence on performance?
Can this problem be solved in the other better way? I know that I can check if it is a main thread using [NSThread isMainThread] method and call dispatch_async only in case of other thread, but it will make me create one more method or block with these UI updates.
There isn't a problem with adding an asynchronous block on the main queue from within the main queue, all it does is run the method later on in the run loop.
What you definitely don't want to do is to call dispatch_sync adding a block to the main queue from within the main queue as you'll end up locking yourself.
Don't worry if you are calling dispatch_async in main thread or not. iOS will put the block in a queue and execute the block in main thread.
Related
I want to call a C function in a Objective-C app. The function contains an endless loop. So I need to run this C function in background.
Here's my C function:
int go(){
for(;;){
//...
}
return 0;
}
And the call:
[self performSelectorInBackground:go() withObject:nil];
The function go() is called but it's not running in background (and the app stop working...).
Even in the background you probably should run something in an endless loop. However it is possible.
[self performSelectorInBackground:<selector> withObject:<Object>];
That is a nice convenience method to just throw a method to the background thread. But you also have access to Grand Central Dispatch that would let you put blocks of code into a background thread as well. You could even give it a private queue so it wouldn't block your background queue.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
// Your code
go();
});
Hmm, there may be an easier way, but...
- (int)doGo {
return go();
}
Then...
[self performSelectorInBackground:#selector(doGo) withObject:nil];
So, this answer really just highlights what I believe is the most fundamental problem with your provided code, but you should certainly see Ryan's answer and use GCD. performSelectorInBackground: really isn't all that great.
We had a bug, and it destroys the looks of our UI, some of the UI elements overlap, or has been added to the subview twice. the bug is hardly reproduced so its hard to fix it. Now I thought of the reason, and probably the method that changes the UI are being called twice at the same time. And I was correct, I tried to create the bug programatically.
We have a bug which is caused by a method being accessed by different threads at the same time. To emulate this problem, and better understand it. see codes posted below.
When I do this, updatePresence Method call, my program works perfectly
ViewController.m
-(void)loadConversationScreen{
[conversationController updatePresence];
}
But when I do this, something goes wrong with my program
ViewController.m
-(void)loadConversationScreen{
[conversationController performSelectorInBackground:#selector(updatePresence) withObject:nil];
[conversationController updatePresence];
}
This is because the method is being accessed at the same time and and the instance of my UIView is being accessed/changed also at the same time.
How do I PROPERLY stop 2 threads from using a method at the same time?
How do I properly handle it in IOS(if there is no proper way, what are the work arounds), are there built in locks or somekind?
My app should support ios 4.0 and up
Advance thanks to all for your help.
The best thread lock for you is #sycnhronized(object) {}. This means only one thread can enter the code at a time. The object passed in is used to perform the lock; only one thread can enter a block protected by a particular object's synchronized at a time. The others will wait. This can be any Objective-C object, even a NSString.
Typically, you'd use whatever object you're trying to protect from multiple threads. You probably want #synchronized(self) {}:
-(void)updateVariables {
#synchronized(self) {
_foo = 1;
_bar = 2;
}
}
#sycnhronized is re-entrant in the sense that the same thread can call #sycnhronized as deeply as it wants, for instance:
- (void)a {
#synchronized(self) {
// entered "immediately" if called from b, where the #synchronized has
// already been called
_foo = _foo + 1;
}
}
- (void)b {
#synchronized(self) {
[self a];
}
}
For posterity and because I already typed it before reading your clarification, if you really cared only about updating the UI, you'd want to force your call over to the main thread instead like this:
- (void)someTask {
dispatch_async( dispatch_get_main_queue(), ^{
[self updateUI];
});
}
- (void)updateUI {
NSAssert( [NSThread isMainThread], #"called from non-main thread" );
// do UI updates here
}
As warrenm said you shouldn't update your UIView from a different thread than the Main thread (UI thread). Still, you asked if there is any workaround for what's going on. To be honest, you should try to, instead of blocking the access of the second thread to your method, understand why the methods is called twice. This is more a logical problem than anything else and you should try to fix that, instead of trying a shortcut.
Say you do
MyLock *lock = [[MyLock new] autorelease];
#synchronized(lock) {
NSLog(#"Hello World");
//some very long process
}
In the main thread. Does that mean till //some very long process is done, the main thread is locked? If some other thread call
//Update on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
//Do some updates
});
That some updates will never be called? Am I correct?
If the code in the first code snippet never finishes, the second one won't be called, regardless of the #synchronized statement. The thread is blocked by the code that you're executing. The #synchronized statement is to synchronize data access between multiple threads and to be useful, it requires that all participating threads actually use the statement. It will not "magically" lock access to the data structure, unless all participating threads "agree" on it.
You don't use #synchronized to ensure that only one method executes on a given (single) thread, that is the case anyhow.
To give you a concrete example of its use, let's say you have an NSMutableArray that you want to protect from getting modified from different threads at the same time (which could lead to data corruption). In that case, you could always access it in a #synchronized block with the same lock token.
Example:
//Thread 1:
#synchronized (myArray) {
[myArray addObject:#"foo"];
}
//Thread 2:
#synchronized (myArray) {
[myArray removeObjectAtIndex:0];
}
This will ensure that the blocks of code that are enclosed by the #synchronized will never execute simultaneously. When one thread enters the block, other threads wait until it finishes, but only if they also use the same #synchronized statement. If you forget to use it on one thread, it doesn't help at all if you used it on the other.
The short answer is no. I think you dont understand the concept of locking. You should read more about syncchronization for example here:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/Multithreading/ThreadSafety/ThreadSafety.html
You have to synchronize using the same locking object (same instance!) in every case when you access the code, which you are trying to protect. You can store the locking object as property of a class.
In your case:
self.lock = [[MyLock new] autorelease]; //in init method initialize retain or strong lock property
...
#synchronized(self.lock) {
NSLog(#"Hello World");
//some very long process
}
//Update on the main thread
dispatch_sync(dispatch_get_main_queue(), ^{
#synchronized(self.lock) {
NSLog(#"Hello World");
//some very long process
}
});
If you can use as the locking object, the object which your are trying to protect.
Im using XMPPFramework and in it's code there's a method like this:
- (NSDictionary *)occupants
{
if (dispatch_get_current_queue() == moduleQueue)
{
return occupants;
}
else
{
__block NSDictionary *result;
dispatch_sync(moduleQueue, ^{//IT BLOCKS HERE, WITHOUT MESSAGE
result = [occupants copy];
});
return [result autorelease];
}
}
[EDIT]
It blocks inconsistently, not always, since the app is not doing anything I pause it and I see the thread has stopped there, and it never continues to execute.
What is wrong? Any ideas?
Thanks
The behavior you explain perfectly matches with the one that appears when you try to send perform an operation on main thread via GCD while being on the main thread. So you should check if moduleQueue is the main queue, then this is it. Try checking if it is the main queue if it is, skip the dispatch_sync block.
Blocks sometimes need to retain variables to ensure they are available when they execute. If you use a local variable inside a block, you should initialise it to zero where you declare it outside the block.
I have two GCD blocks that are async. The first is for the background thread, the second runs on the main thread. This works great, but I just saw somewhere talking that I might need to release them using dispatch_release(). E.g.:
// Use gcd
dispatch_queue_t queue = dispatch_queue_create("com.awesome", 0);
dispatch_queue_t main = dispatch_get_main_queue();
// do the long running work in bg async queue
// within that, call to update UI on main thread.
dispatch_async(queue, ^{
// Do work in the background
// Release
dispatch_release(queue);
dispatch_async(main, ^{
// Main
// Release
dispatch_release(main);
});//end
});//end
Is this true? Do I need to release them here?
You only need to release the queue created with dispatch_queue_create. The main queue will always exist, and it doesn't make sense to release it.
Any blocks added to the queue will retain the queue itself, so you can safely call dispatch_release(queue) after your dispatch_async call. Best to do this outside the block after the code you've written here.
Only release queues that you create; don't release the main queue or the global concurrent queues (or, again, any you did not create yourself). It's also not a good idea to nest the release within the work block enqueued on that queue, as you're doing, because that's doing it in the wrong scope and this:
queue = dispatch_queue_create(...)
dispatch_async(queue, ^{ something; dispatch_release(queue); });
dispatch_async(queue, ^{ something else}); // CRASH!
Won't work when you later change the code to add that 2nd dispatch_async(). Always pairing your create/release calls in the same scope, assuming that you can, is a better stylistic choice.