If a second method is called from a method that is running on a background thread, is the second method automatically ran in that same thread or does it happen back on the main thread?
Note: I want my second method to be handled in the background, but since I update the UI inside it, would doing the following, be the right way to do it:
- (void)firstMethod {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
//Do stuff in background
...
//Call a second method (Assume it'll run in this background thread as well)
[self secondMethod];
});
}
//Second Method
- (void)secondMethod {
//Do heavy lifting here
...
dispatch_async(dispatch_get_main_queue(), ^{
//Update UI here
...
});
}
Update
Oh I totally forgot to mention that this method is something that loads suggestions into the view (think keyboard suggestions). Since every key tap would be calling this method, I only want it to run when a user has finished typing. The way I'm approaching it is by setting a 0.2 delay between keys and if a new key tap fall within that 0.2 delay it cancels the previous method call, and initiates a new call (this way, assuming the use types the word, "the", it doesn't run suggestions for "t", "th", "the". Since the user is typing pretty quickly we can assume they don't want suggestions for anything until they have stopped typing (allowing the call to go through after a 0.2s delay), or if they type slow (where they probably are looking for suggestions along the way).
So when calling my secondMethod I do the following:
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(secondMethod) object:nil];
[self performSelector:#selector(secondMethod) withObject:nil afterDelay:0.2];
The problem is it's not being called (I'm assuming this method defaults it to be performed in the main thread?)
Generally speaking, nothing is going to hop between threads without being pretty explicit about it. Certainly something as trivial as just calling a method isn't. Your code seems fine. Remember to not access mutable state from more than one queue at once (for example if the heavy lifting uses instance variables, make sure that -firstMethod doesn't get called twice in a row. It'd spawn off two async calls to -secondMethod then, and they'd step all over each others data. If that's a problem, create a serial dispatch queue instead of using a global one).
Related
I'm currently learning the runloop mechanism in iOS. After reading Run, RunLoop, Run! and the CFRunloop source code, I'm still confused about how it really works. One of my confusions is about the CFRunLoopPerformBlock() function. Many articles mentioned that this function will enqueue the block and execute it in the next runloop, but my question is: what does the block mean here?
Let's say I have a very simple CustomViewController.
- (void)viewDidLoad
{
[super viewDidLoad];
UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(0, 50, 100, 100)];
redView.backgroundColor = [UIColor redColor];
[self.view addSubview:redView];
}
Apparently there's no block syntax in this code. Will viewDidLoad be called by CFRunLoopPerformBlock() ? If not, how is this snippet handled by runloop?
Apparently there's no block syntax in this code. Will viewDidLoad be called by CFRunLoopPerformBlock()? If not, how is this snippet handled by runloop?
The viewDidLoad has practically nothing to do with CFRunLoopPerformBlock. The viewDidLoad is just a method that is called in our view controller when the view has been loaded, but before it’s been presented in the UI, to give us a chance to configure our UI.
So what is the run loop? It is just a loop that is constantly running, checking for various events (events, timers, etc.). It’s running behind the scenes in every iOS app, though we rarely interact directly with it nowadays. (The exception might be when we start certain types of timers, we add them to the main run loop. But that’s about it nowadays.) But when we return from methods like viewDidLoad, we’re yielding control back to the run loop.
what does the block mean here?
A “block” (also known as a “closure” in Swift) is just a piece of code to be run, when this code block is stored in a variable or used as a parameter of a method. The CFRunLoopPerformBlock function effectively says, “here is a some code to run during the next iteration of the run loop”. The third parameter of that function is the code to be run, and is the “block” of code (in Objective-C it starts with ^{ and ends with the final }). For information about Objective-C blocks, see Apple's Blocks Programming Topics or Programming with Objective-C: Working with Blocks.
All of this having been said, it’s worth noting that one wouldn’t generally use CFRunLoopPerformBlock. If we want to dispatch a piece of code to be run, we’d generally now use Grand Central Dispatch (GCD). For example, here is some code that has two parameters, a queue and a block:
dispatch_async(dispatch_get_main_queue(), ^{
self.label.text = #"Done";
});
Again, everything from the ^{ to the } is part of that second parameter, which is the block. This code says “add this block of code that updates the text of the label to the main queue.”
According to Apple documentation,
This method enqueues a block object on a given runloop to be executed as the runloop cycles in specified modes.
This method enqueues the block only and does not automatically wake up the specified run loop. Therefore, execution of the block occurs the next time the run loop wakes up to handle another input source. If you want the work performed right away, you must explicitly wake up that thread using the CFRunLoopWakeUp function.
You can pass a block of code in it as
CFRunLoopPerformBlock(CFRunLoopGetMain(), kCFRunLoopCommonModes, ^{
// your code goes here
});
I've inherited some code that has this rather unusual nested sequence. The usual paradigm would have a single dispatch to the main queue to update the UI. The code, shown below, nests a dispatch to the main queue within another dispatch to the main queue.
- (void)viewDidLoad
{
// Setup some data
// Adjust UI
dispatch_async(myBackgroundQueue, ^{
while(Do_some_time_consuming_work) {
// Time consuming work goes here
if (things_are_going_slowly) {
dispatch_async(dispatch_get_main_queue(), ^{ // <- one of these two seems redundant
dispatch_async(dispatch_get_main_queue(), ^{ // <- one of these two seems redundant
stillWorkingLabel.hidden = NO; //Let user know the work is still ongoing
});
});
)
// Finish time-consuming work
}
});
}
What is the purpose, if any, of nesting dispatch_async(dispatch_get_main_queue()? This nested sequence shows up in several places in the app. It seems to me that only one dispatch to the main queue is needed.
I think I've read all of the relevant questions on related topics here and via Google search, but I haven't found anyone suggesting nesting two identical dispatches.
The app works well, with the UI updating as expected in the above example and in other places in the code.
Most of the app's code uses the usual non-nested version of the above scheme, and of course it also works just fine.
I'm inclined to just replace these nested calls with a single dispatch, but maybe I'm missing something here. Any advice would be appreciated.
I can't think of a single advantage to doing this, and can think of reasons not to. It will delay the execution of the inner closure, as well as taking a small amount of additional resources. (It's going to force the app to go through at least 2 passes through the event loop before the work item gets executed.)
I think removing the nested calls is the right thing to do.
Nesting two dispatch_queues for the main doesn't make any sense , the straight forward way is that
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// perform long task here
dispatch_async(dispatch_get_main_queue(), ^(){
//Add method, task you want perform on mainQueue
//Control UIView, IBOutlet all here
}); });
I have a ViewController that is set as delegate of MKMapView and CLLocationManager. This ViewController also receives actions from UI buttons.
I have a state machine that needs to be updated whenever some UI action happens or when a new location is available (whether it comes from MKMapView or CLLocationManager)
Obviously, the state machine update must be atomic, but I can't seem to find a good mechanism to do so.
Here's a snippet of the code:
m_locationSerialQueue = dispatch_queue_create("locationSerialQueue_ID", NULL);
m_fsmSerialQueue = dispatch_queue_create("fsmSerialQueue_ID", NULL);
...
- (void)locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
dispatch_sync(m_locationSerialQueue, ^{
...
[self updateFSMWithAction:LocationUpdated];
});
}
- (void)mapView:(MKMapView *)mapView didUpdateUserLocation:(MKUserLocation *)userLocation
{
dispatch_sync(m_locationSerialQueue, ^{
...
[self updateFSMWithAction:LocationUpdated];
});
}
- (IBAction)someButton:(id)sender
{
...
[self updateFSMWithAction:buttonPressed];
}
- (void)updateFSMWithAction:(enum action_t)action
{
dispatch_sync(m_fsmSerialQueue, ^{
...
});
}
I logged [NSThread currentThread] and all my logs have the same thread as currentThread (that is, neither MKMapView nor CLLocationManager are calling the delegate methods from different threads, it seems there is only one thread) which, unless I am missing something, will obviously prevent me from using any synchronization mechanism.
EDIT_Nov19: added more code to show the use of serial GCD queues, however I occasionally get deadlocks (most of the time when interacting with the UI), which is likely to be happening because everything is happening on a single thread, right?
According to what I read (and understood) from the doc, it should be valid to do the equivalent of:
dispatch_sync(firstSerialQueue, ^{
dispatch_sync(secondSerialQueue, ^{
...
});
});
But maybe I'm missing something regarding GCD? Actually, I'm new to it and I would prefer using standard stuff like threads, mutexes, semaphores, etc. But I'm mainly interested on a generic solution because I doubt I am the first one to encounter these kind of issues.
Are there any good practices to follow in order to synchronize delegates?
EDIT_Nov21: I've been trying to imagine how does a Cocoa app works and drawing the parallel with a basic C program on any OS, and I think I'm beginning to understand what could be happening.
A C program has a main(...) function and when the app is launched, the OS creates a process (not going into details) and then calls main(...). When main returns, the OS undoes what it did to create the process.
Cocoa takes over the main and enters into a loop (which is only exited if the app tells Cocoa it will exit).
Most likely this master loop handles I/O (touch, mouse, display), and when there are events (touches), it automagically calls the view controller associated with the current view. I say automatically because there must be lots happening under the hood, like any time a view is added to the view hierarchy, it's probably added to some internal queue of that master loop.
The master loop goes to its queue and checks if there's something to signal, if it is, it calls the appropriate functions. So far, this could be just one big thread.
Now, some objects (like CLLocationManager) must have their own threads, but apparently, instead of calling the delegates from their own threads, they must be somehow adding the calls to some queue on the master loop, which then goes on to call the functions. This would explain why all my functions are called from within the same thread (this might seem "duh!" for many Cocoa experienced people, but I find that quite surprising and the opposite of intuitive)
Is it a convention to call all delegates on the 'master loop'?
I would imagine that the master loop also handles the display updates, and thus can process queued operations at once (does it stops accepting requests or does it work with a copy of the queue?, it probably does something like that in order to avoid having a corrupted display update), resulting in a sort of "atomic" update.
If that's the case, then it could make sense that the calls to the delegates are scheduled to happen on the master loop, just in case an UI update is made.
However, if CLLocationManager can setup a call to its delegate to happen from within the master loop, why couldn't the UI objects do the same under the hood? Furthermore, they are probably adding operations to some queue of the master loop anyway.
EDIT_Nov21_2:
Maybe scheduling the calls to the delegates to happen from within the master loop they are attached to is to avoid having people use mutexes, etc. to sync calls? Although it is a possibility, it does not seems consistent with my logs. Have to keep digging.
I am aware that blocks are one of the latest feature added in ios. But I am really finding a
tough time learning it .
I have seen people doing the following
typedef void(^CallBackBlk) (NSString *);
#property(copy,nonatomic)CallBackBlk block;
and in .m class
-(void)doSomething:(CallBackBlk )cb{
self.block=cb;
}
I never understood what is the use of assigning it to cb here. Can't I simply do the following
-(void)doSomthing{
block(#"my string");
}
I am really not getting the purpose of storing the block in instance variable. Can any help
me with an example. Any help is greatly appreciated
In your doSomething method, where does block come from?
Answer that, and you'll have your reason.
Ah -- the commentary makes the question clear. Snark served a purpose (snark and too lazy to type out a real answer on my iPhone at 7AM :).
An instance variable is just a slot to put things. Nothing is in that slot to start with.
In your case, you could implement:
-(void)doSomething:(CallBackBlk )cb{
cb();
}
However, typically, a callback is used when you do something asynchronously. For example, you might do:
[myObject doSomething:^{
NSLog(#"did something");
}];
And then:
-(void)doSomething:(CallBackBlk)cb {
dispatch_async(... global concurrent queue ..., ^{
... do some work ...
cb();
});
}
That is, doSomething: will return as soon as the dispatch_async() happens. The callback block is used to callback to let you know that asynchronous operation is done.
Of course, still no need for an instance variable. Take that class that does something a bit further; make it some kind of relatively complex, state transitioning, engine. Say, like your average internet downloader or compute heavy simulation engine. At that point, lumping all your background work into a single method would be overly complex and, thus, shoving the callback block(s) (there may likely be more than one; a progress updater, a completion block and/or an error block, for example) into instance variables allow the class's implementation to be subdivided along lines of functionality more cleanly.
What is the use of storing the block in an instance variable
Perhaps to be able to access it later?
You would do that if you want to invoke the block later, after the method that assigns it has already returned.
Consider for example an object that manages a download. You might want to have a block that gets invoked when the download completes (e.g. to update the UI), but you don't want the download method to have to wait until that happens (because it might take a long time).
maybe and example of use will help..
one use for storing it as a variable i have found is if you have multiple views that all access another view (for me it was a map on the next view) i used blocks that were setup by the previous view (set the default location for the map, initialise markers and so forth) then passed it through to the next view, where it would run it, setting up the map. it was handy having the block use the local variables of the previous view to access certain attributes. it wasnt the only way to do it, but i found it was a nice clean way of going about it.
and here is an example of what gets run in the viewDidLoad of the mapview
if(setupMap){
setupMap(mapView);
}
if(gpsUpdate){
gpsUpdate(mapView);
}
if(addMarker){
addMarker(mapView);
}
now if those blocks were assigned (the if statement check if they are nil), it would run them and do the appropriate setup for the map. not every view needed to do those, so they would only pass to the map view what needed to be done. this keeps the map view very general purpose, and code gets reused a lot. write once use lots!
To use the block, you call your doSomething: method:
CallBackBlk laterBlock = ^(NSString *someString) {
NSLog(#"This code is called by SomeClass at some future time with the string %#", someString);
};
SomeClass *instance = [[SomeClass alloc] init];
[instance doSomething:laterBlock];
As you code the implementation of your class, it will presumably reach some condition or finish an action, and then call the laterBlock:
if (someCondition == YES) {
self.block("Condition is true");
}
EDIT:
Today I worked with my code a bit and gave background thread task some advantages over the main thread task. Essentially one thread is working with the even numbered items in an array and the other is working with the odd numbers. I gave the background thread the even number half, which will be either equal to or one less than the odd half. Also, I moved the while(!collisionDone) to a bit later in the code, as far back as I can to keep it thread safe. I put an NSLog in there to detect if the condition is ever false when it reaches that point, and it has not fired once. Furthermore, the instruments build now runs fine. That means the problem is stalling the main thread with the while loop. So that means my question now is:
How should I stall the main thread to wait until the background thread completes a task? Perhaps demonstrate how one uses NSLock to achieve that functionality?
Today I added multithreading in an attempt to reduce lag in my game. I am using an NSTimer to call my time loop function like this:
[NSTimer scheduledTimerWithTimeInterval:0.03 target:self selector:#selector(time:) userInfo: nil repeats: true];
Then, in that function, I have:
//run collision using multithreading
collisionDone = false;
[self performSelectorInBackground:#selector(collideBackground:) withObject:objectToSend];
[self collideMain:objectToSend];
while (!collisionDone) {
//notdone
}
The idea is that I am running one set of collision checks on one thread and another set in another thread. The empty while loop is to ensure the background thread has completed before carrying on in the function. After this bit, the code goes on to update the view.
This all runs fine, normally, but when I went to Instruments to check on my FPS, I found that the game freezes, it appears that it is freezing somewhere in the first run of the time loop. The app ran fine in Instruments before the multithreading, which essentially worked the same, except simply using [self collideBackground:objectToSend] rather than sending it to another thread. It would appear that the background thread is not completing or not being run at all, so collisionDone will always be false, and therefore the app will wait in that endless loop until I kill it.
Also, I have tried replacing that line with [self collideBackground:arrayToSend];, and it starts working again. Another thing I tried is putting NSLog(#"called"); at the very start of viewWillAppear: and it seems to run exactly one more frame!?!?!?
I would like any ideas as to why exactly this is happening and how to fix it. I doubt it is unrelated to the multithreading as doing everything on a single thread fixes it.
Yes it is related to multithreading, and your poll on the main thread. You should put a callback into your background thread instead. And not blocking the main thread.
// is not good at all
collisionDone = false;
/* etc... that will make collision to true */
while (!collisionDone) {
}
make instead
[self computeSomethingOnBagroundWhithData: nil onCompletion:^{
// callBack stuff
}]
Edit:
while (!collisionDone)
is always true because when you are creating another thread, the instruction is stuck in "while" statement, and is not reevaluating each time "collisionDone" as you would expect, but evaluate it inside the while scope. By the way, there could be some optimizer stuff, and "collisionDone" could only be evaluated in local scope (but here I'm only guessing, since it's compiler dependent). You should reenter the method in order to make it work, or join the thread when you are done. Since you don't want complex code for this kind of stuff, block are exactly what you want. libDispatch are perfect for this.
So you can try "self.collisionDone" in order to try to get the genuine pointer. But there's lot of chance that won't change anything.
Or you can do your stuff in background and when you are done, send a signal / callback to your client code. You don't need to make polling like you did. This is really bad design (really trust me ;) )
It has been unclear what the best approach to solving this problem or what the actual cause of the problem (yes it is that while(!collisionDone) loop, but why?), but none of the proposed alternatives can do the job properly. Although other options have been suggested, NSThread and performSelectorInBackground: have been the only ways I have detected the functionality I am looking for. Furthermore, after testing with the multithreading part turned off, I realized that the iPhone 4s has a much, much better processor than the iPhone 4, and I can run the game at 60 FPS on the iPhone 4s and newer without multithreading (the iPhone 4 can only handle 30 FPS). Lastly, I intended for my multithreading to utilize the dual-core processor of the iPhone 4s, so half of the collision would be done in one core while the other core does the other half. I am not sure this is actually happening and I have no idea how to detect this.
My conclusion: Remove this attempt at multithreading from the app completely. It is unnecessary and the benefits are dubious.
#Fonix: The question you linked to shows exactly what I am trying to do, however, none of the solutions they presented seem to be working.
The final solution after talking to my programmer friend about it at school:
//threadLock is an NSLock declared in the .h
-(void)timeLoop {
[self performSelectorInBackground:#selector(collideBackground) withObject:nil];
[self collideMainAtStartingPoint:0];
[threadLock lock];
[threadLock unlock];
//update the view hierarchy and stuff like that
}
-(void)collideBack {
[threadLock lock];
[self collideMainAtStartingPoint:2];
[threadLock unlock];
}