Order of operations in runloop on iOS - ios

What is the order of operations on iOS?
I'm thinking sepcifically about timing of
setNeedsLayout and layoutSubviews
setNeedsDisplay and drawRect
touch recognition
[NSTimer scheduledTimerWithTimeInterval:0.000001 tar(...)]
dispatch_async(dispatch_get_main_queue(), ^{ /* code */}
As an example of an answer I would like to receive it could be in this format:
dispatch_async on main Happens before the next runcycle
drawRect Happens at the end of the runcycle

(Parts of this are copied from my answer to a similar question.)
It turns out that the run loop is complicated, and a simple question like “Does drawRect: happen at the end of the runcycle?” doesn't have a simple answer.
CFRunLoop is part of the open-source CoreFoundation package, so we can take a look at exactly what it entails. The run loop looks roughly like this:
while (true) {
Call kCFRunLoopBeforeTimers observer callbacks;
Call kCFRunLoopBeforeSources observer callbacks;
Perform blocks queued by CFRunLoopPerformBlock;
Call the callback of each version 0 CFRunLoopSource that has been signaled;
// Touch events are a version 0 source in iOS 8.0.
// CFSocket is a version 0 source.
if (any version 0 source callbacks were called) {
Perform blocks newly queued by CFRunLoopPerformBlock;
}
if (I didn't drain the main queue on the last iteration
AND the main queue has any blocks waiting)
{
remove all blocks from the main queue
execute all the blocks just removed from the main queue
} else {
Call kCFRunLoopBeforeWaiting observer callbacks;
// Core Animation uses a BeforeWaiting observer to perform layout and drawing.
Wait for a CFRunLoopSource to be signalled
OR for a timer to fire
OR for a block to be added to the main queue;
Call kCFRunLoopAfterWaiting observer callbacks;
if (the event was a timer) {
call CFRunLoopTimer callbacks for timers that should have fired by now
} else if (event was a block arriving on the main queue) {
remove all blocks from the main queue
execute all the blocks just removed from the main queue
} else {
look up the version 1 CFRunLoopSource for the event
if (I found a version 1 source) {
call the source's callback
}
// Interface orientation changes are a version 1 source in iOS 8.0.
}
}
Perform blocks queued by CFRunLoopPerformBlock;
}
Core Animation registers a kCFRunLoopBeforeWaiting observer with an order of 2000000 (although that is not documented; you can figure it out by printing [NSRunLoop mainRunLoop].description). This observer commits the current CATransaction, which (if necessary) performs layout (updateConstraints and layoutSubviews) and then drawing (drawRect:).
Note that the run loop can evaluate the true in while(true) twice before executing BeforeWaiting observers. If it dispatches timers or a version 1 source, and that puts block on the main queue, the run loop will go around twice before calling the BeforeWaiting observers (and it will dispatch version 0 sources both times).
The system uses a mixture of version 0 sources and version 1 sources. In my testing, touch events are delivered using a version 0 source. (You can tell by putting a breakpoint in a touch handler; the stack trace contains __CFRunLoopDoSources0.) Events like entering/leaving foreground are dispatched through CFRunLoopPerformBlock, so I don't know what kind of source really provides them. Interface orientation changes are delivered through a version 1 source. CFSocket is documented to be a version 0 source. (It's likely that NSURLSession and NSURLConnection use CFSocket internally.)
Note that the run loop is structured so only one of these branches happens on each iteration:
Ready timers fire, or
Blocks on dispatch_get_main_queue() run, or
A single version 1 source is dispatched to its callback.
After that, any number of version 0 sources can call their callbacks.
So:
Layout always happens before drawing, if both are pending when the Core Animation observer runs. The CA observer runs after timers, main queue blocks, or the external event callback have run.
The main GCD queue has seniority over timers and version 1 sources, if the run loop didn't drain the main queue on the prior turn of the loop.
Timers have seniority over the main queue and version 1 sources, should all three be ready.
The main queue has seniority over version 1 sources, should both be ready.
Also remember that you can request immediate layout at any time using layoutIfNeeded.

One task after the other is added to the runloop from various sources; the runloop will execute the oldest task on the runloop and not start another task until the call for that task returns.

Handling of user interaction
UI Components call setNeedsLayout and setNeedsDisplay if they need an update
Layouting is done using layoutSubviews (called indirectly by layoutSublayers)
Painting is done using drawRect and drawInContext:
dispatch_async call is performed
Your timer with 0.000001 seconds delay could be executed before or after the dispatch_async. Difficult to say.
1 and 2 are actually mixed because it's mostly the user interaction causing changes in the UI by calling setNeedsLayout and setNeedsDisplay somewhere.
The order of 1, 2, 3 and 4 is well-defined. 5 should also always happen afterwards. NSTimer depends on various circumstances - you should not rely that it's called before or after the dispatch_async call but it most likely will be executed after the painting is done.

Related

DispatchQueue crashing with main.sync in Swift

Please explain to me why I am getting this crash?
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
in this
DispatchQueue.main.sync {
print("sync")
}
This is my code.
override func viewDidLoad() {
super.viewDidLoad()
print("Start")
DispatchQueue.main.async {
print("async")
}
DispatchQueue.main.sync {
print("sync")
}
print("Finish")
}
NEVER call the sync function on the main queue
If you call the sync function on the main queue it will block the queue as well as the queue will be waiting for the task to be completed but the task will never be finished since it will not be even able to start due to the queue is already blocked. It is called deadlock.
Two (or sometimes more) items — in most cases, threads — are said to be deadlocked if they all get stuck waiting for each other to complete or perform another action. The first can’t finish because it’s waiting for the second to finish. But the second can’t finish because it’s waiting for the first to finish.
You need to be careful though. Imagine if you call sync and target the current queue you’re already running on. This will result in a deadlock situation.
Use sync to keep track of your work with dispatch barriers, or when you need to wait for the operation to finish before you can use the data processed by the closure.
When to use sync?
When we need to wait until the task is finished. F.e. when we are making sure that some function/method is not double called. F.e. we have synchronization and trying to prevent it to be double called until it's completely finished.
When you need to wait for something done on a DIFFERENT queue and only then continue working on your current queue
Synchronous vs. Asynchronous
With GCD, you can dispatch a task either synchronously or asynchronously.
A synchronous function returns control to the caller after the task is completed.
An asynchronous function returns immediately, ordering the task to be done but not waiting for it. Thus, an asynchronous function does not block the current thread of execution from proceeding on to the next function.
#sankalap, Dispatch.main is a serial queue which has single thread to execute all the operations. If we call "sync" on this queue it will block all other operations currently running on the thread and try to execute the code block inside sync whatever you have written. This results in "deadlock".
As per Apple documentation on executing dispatch_sync on a queue you're currently on will crash your code:
Calling this function and targeting the current queue results in
deadlock.
Because the current queue is the main queue, when you continue to call sync on the main queue, the system will understand that current main queue must wait some code complete in current queue, but no code at current queue (main queue), so you wait forever:
Apple document: Calling this function and targeting the current queue results in deadlock.

Why we need the synchronous operation in ios

I want to know As we all know how asynchronous task are necessary for concurrency but Wanted to know why we need the synchronous tasks. while we can achieve the same with the normal usage of function.
Thanks & regards
Rohit
When you calls something synchronously, it means that 'the thread that initiated that operation will wait for the task to finish before
continuing'. Asynchronous means that it will not wait for finish the task.
synchronous calls stops your current action and returns when the call returned. with asynchronous calls you can continue.
synchronous is the opposite of asynchronous code, and therefore is ordinary code.
At the end, if asynchronous is totally out of scope then you will not emphasize the word synchronous.
It helps to synchronise threads, as the name suggests.
consider a typical usage of GCD async and sync (pseudo)
async background_thread {
//1 call webservice or other long task that would block the main thread
sync main_thread {
//2 update UI with results from 1
}
//3 do something else that relies on 2
}
now if 2 was in an async and you needed to do something at 3 that relies on the updates at 2 to have happened, then you are not guaranteed (and most likely wont) get the behaviour you are expecting. instead, you use a sync to make sure that the task is completed before continuing the execution in the background thread.
If you are asking now, why not just take out the sync/async around 2 so it executes in order anyway? the problem is, the UI must not be updated on a background thread otherwise the behaviour is undefined (which usually means the UI lags a lot). So in essence what happens is the background thread waits at 2's sync until the main thread gets round to executing that block, then it will continue with the rest of the execution on the background thread.
If you were dealing with a task that doesnt require the main thread (or some other thread) to execute properly, then yes you may as well take out the sync at 2.
This is just one example of how a sync is useful, there are others if you are doing advanced threading in your app.
Hope this helps
Typically it's because you want to do an operation on a specific different thread but you need the result of that operation. You cannot do the operation asynchronously because your code will proceed before the operation on the other thread completes.
Apple has a very nice example:
func asset() -> AVAsset? {
var theAsset : AVAsset!
self.assetQueue.sync {
theAsset = self.getAssetInternal().copy() as! AVAsset
}
return theAsset
}
Any thread might call the asset method; but to avoid problems with shared data, we require that only functions that are executed from a particular queue (self.assetQueue) may touch an AVAsset, so when we call getAssetInternal we do it on self.assetQueue. But we also need the result returned by our call to getAssetInternal; hence the call to sync rather than async.

Can i use GCD for background thread and performSelector for resume back to main thread? [duplicate]

I've used both GCD and performSelectorOnMainThread:waitUntilDone in my apps, and tend to think of them as interchangeable--that is, performSelectorOnMainThread:waitUntilDone is an Obj-C wrapper to the GCD C syntax. I've been thinking of these two commands as equivalent:
dispatch_sync(dispatch_get_main_queue(), ^{ [self doit:YES]; });
[self performSelectorOnMainThread:#selector(doit:) withObject:YES waitUntilDone:YES];
Am I incorrect? That is, is there a difference of the performSelector* commands versus the GCD ones? I've read a lot of documentation on them, but have yet to see a definitive answer.
As Jacob points out, while they may appear the same, they are different things. In fact, there's a significant difference in the way that they handle sending actions to the main thread if you're already running on the main thread.
I ran into this recently, where I had a common method that sometimes was run from something on the main thread, sometimes not. In order to protect certain UI updates, I had been using -performSelectorOnMainThread: for them with no problems.
When I switched over to using dispatch_sync on the main queue, the application would deadlock whenever this method was run on the main queue. Reading the documentation on dispatch_sync, we see:
Calling this function and targeting
the current queue results in deadlock.
where for -performSelectorOnMainThread: we see
wait
A Boolean that specifies whether the
current thread blocks until after the
specified selector is performed on the
receiver on the main thread. Specify
YES to block this thread; otherwise,
specify NO to have this method return
immediately.
If the current thread is also the main
thread, and you specify YES for this
parameter, the message is delivered
and processed immediately.
I still prefer the elegance of GCD, the better compile-time checking it provides, and its greater flexibility regarding arguments, etc., so I made this little helper function to prevent deadlocks:
void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
if ([NSThread isMainThread])
{
block();
}
else
{
dispatch_sync(dispatch_get_main_queue(), block);
}
}
Update: In response to Dave Dribin pointing out the caveats section ondispatch_get_current_queue(), I've changed to using [NSThread isMainThread] in the above code.
I then use
runOnMainQueueWithoutDeadlocking(^{
//Do stuff
});
to perform the actions I need to secure on the main thread, without worrying about what thread the original method was executed on.
performSelectorOnMainThread: does not use GCD to send messages to objects on the main thread.
Here's how the documentation says the method is implemented:
- (void) performSelectorOnMainThread:(SEL) selector withObject:(id) obj waitUntilDone:(BOOL) wait {
[[NSRunLoop mainRunLoop] performSelector:selector target:self withObject:obj order:1 modes: NSRunLoopCommonModes];
}
And on performSelector:target:withObject:order:modes:, the documentation states:
This method sets up a timer to perform the aSelector message on the current thread’s run loop at the start of the next run loop iteration. The timer is configured to run in the modes specified by the modes parameter. When the timer fires, the thread attempts to dequeue the message from the run loop and perform the selector. It succeeds if the run loop is running and in one of the specified modes; otherwise, the timer waits until the run loop is in one of those modes.
GCD's way is suppose to be more efficient and easier to handle and is only available in iOS4 onwards whereas performSelector is supported in the older and newer iOS.

Which queue is used for -[NSObject performSelector:withObject:afterDelay]?

I recently ran into an issue where deferred selectors weren't firing (an NSTimer and methods called with performSelector:withObject:afterDelay).
I've read Apple's documentation, and it does mention in the special considerations area,
This method registers with the runloop of its current context, and depends on that runloop being run on a regular basis to perform correctly. One common context where you might call this method and end up registering with a runloop that is not automatically run on a regular basis is when being invoked by a dispatch queue. If you need this type of functionality when running on a dispatch queue, you should use dispatch_after and related methods to get the behavior you want.
This makes perfect sense, except for the runloop of its current context part. I found myself confused regarding which runloop it's actually going to. Would it be the thread's main runloop that processes all events, or could it be a different one without our knowledge?
For instance, if I hit a breakpoint before calling performSelector inside a block that is being called as a CoreAnimation completion block, the debugger shows execution is on the main thread. However, calling performSelector:withObject:afterDelay never actually runs the selector. This makes me think that call is effectively registering with the runloop associated with the CoreAnimation framework, so regardless of the performSelector call being executed on the main thread, if the CoreAnimation doesn't poll its runloop, the operation isn't executed.
Replacing this call inside that block with performSelectorOnMainThread:WithObject:waitUntilDone fixes the problem, but I've had a hard time convincing a colleague that this is the root cause.
Update: I was able to trace back the origin of the issue to a UIScrollViewDelegate callback. It makes sense that when a UI delegate callback is invoked that the main runloop would be in UITrackingRunLoopMode. But at that point, the handler will queue a block on a background queue and from there execution will jump across a few other queues, eventually coming back to the main runloop. The catch is that when it comes back to the main runloop, it's still in UITrackingRunLoopMode. I think that the main runloop should have come out of UITracking mode when the delegate method was completed, but when execution gets back to main runloop, it's still in that mode. Deferring the code that kicks off the background queueing of the job from the UIScrollViewDelegate method fixes the problem, e.g [self performSelector:#selector(sendTaskToBackQueue) withObject:nil afterDelay:0 inModes:#[NSDefaultRunLoopMode]]. Is it possible that the runloop mode that is used when the background task is queued back to the main thread is dependent on the mode the runloop was in when it queued the background task?
Essentially, the only change was going from this...
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
// Currently in UITrackingRunLoopMode
dispatch_async(someGlobalQueue, someBlock);
// Block execution hops along other queues and eventually comes back to main runloop and will still be in tracking mode.
}
to this
- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate {
// Currently in UITrackingRunLoopMode
[self performSelector:#selector(backQueueTask) withObject:nil afterDelay:0 inModes:#[NSDefaultRunLoopMode]];
}
-(void)backQueueTask {
// Currently in NSDefaultRunLoopMode
dispatch_async(someGlobalQueue, someBlock);
// Hops along other queues and eventually comes back to main runloop and will still be in NSDefaultRunLoopMode.
// It's as if the runloop mode when execution returns was dependent on what it was when the background block was queued.
}
There is only one run loop per thread, so if you're on the main thread then you're also on the main run loop. However, a run loop can run in different modes.
There are a few things you can try to get to the bottom of the issue:
You can use +[NSRunLoop currentRunLoop] and +[NSRunLoop mainRunLoop] to verify that you're executing from the main thread and main run loop.
You can also use the current run loop directly with NSTimer to schedule a delayed perform-selector. E.g.:
void (^completionBlock)(BOOL) = ^(BOOL finished) {
NSCAssert([NSRunLoop currentRunLoop] == [NSRunLoop mainRunLoop], #"We're not on the main run loop");
NSRunLoop* runLoop = [NSRunLoop mainRunLoop];
// Immediate invocation.
[runLoop performSelector:#selector(someMethod) target:self argument:nil order:0 modes:#[NSDefaultRunLoopMode]];
// Delayed invocation.
NSTimer* timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:#selector(someMethod) userInfo:nil repeats:NO];
[runLoop addTimer:timer forMode:NSDefaultRunLoopMode];
};
Those calls are essentially equivalent to -performSelector:withObject: and -performSelector:withObject:afterDelay:.
This allows you to confirm which run loop you're using. If you're on the main run loop and the delayed invocation doesn't run, it's possible that the main run loop is running in a mode that doesn't service timers in the default mode. For example, that can happen when a UIScrollView is tracking touch input.
-performSelector:withObject:afterDelay: doesn't schedule operations on a dispatch queue; it schedules it on the run loop of the current thread. Each thread has one run loop, but somebody has to run the run loop in order for it to execute the actions on it. So it all depends on what thread this code is run on.
If it is run on the main thread, the operation will be scheduled on the main thread's run loop. In event-based applications, UIApplicationMain is called in the main function, which runs a run loop on the main thread for the entire lifetime of the app.
If this is run on another thread that you created, then the operation will be put on that thread's run loop. But unless you explicitly run the thread's run loop, the operations scheduled on the run loop won't run.
If this is run on a GCD dispatch queue, it means it is running on some unknown thread. GCD dispatch queues manage threads internally in a way that is opaque to the user. Generally nobody would have run the run loop on such a thread, so operations scheduled on the run loop won't run. (Of course, you could explicitly run the run loop in the same place that you schedule the operation, but that would block the thread, and thus block the dispatch queue, which wouldn't make that much sense.)
performSelector:withObject:afterDelay this will call the selector on the thread that this function is called.
performSelectorOnMainThread:WithObject:waitUntilDon,this will make sure that the selector is called on main thread
What is run loop:
Run loops are part of the fundamental infrastructure associated with threads. A run loop is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.

Completion block thread

I have this piece of code:
[[FBController sharedController] getUserDetailsWithCompletionBlock:^(NSDictionary *details)
{
// Updating UI elements
}];
I don't understand a thing: when the block is fired, the secondary thread is still running. Isn't it more correct that the completion of a block should be executed on main thread automatically?
I know that I am wrong with something and I need a couple of explanations.
The Facebook SDK documentation should give you more details, but in general a well-behaved SDK would call completion blocks on the same thread that the SDK was called from. Any long-running or asynchronous operations that the SDK may perform should operate on a separate thread, usually only visible to the SDK. Whether or not that separate thread is still running or not, is an implementation detail of the SDK - and you shouldn't care about it from the client code perspective.
You can visualise it like this:
Client Code (Main Thread) : [Request]--[Response]-[Continue Thread]-------[Completion Block]
v ^ ^
SDK Code (Main Thread) : [Immediate Operations] |
v |
SDK Code (Private Thread) : [Long Running / Asynchronous Operations]----[Finished]
In the specific example you posted, there's no 'Response' from the getUserDetailsWithCompletionBlock method, so the thread carries on as usual.
The missing piece to the jigsaw puzzle might be - "How does my completion block get executed on the main thread". Essentially this comes down to the Runloop system. Your main thread isn't actually owned and operated by your code, it's behind the scenes. There's a Main Runloop which periodically looks for things to do. When there's something to do, it operates those somethings on the main thread sequentially. When those somethings have finished, it goes back to looking for something else to do. The SDK basically adds your completion block to the main runloop, so the next time it fires, your block is there waiting to be executed.
Other things that the runloop might be doing are:
UI Updates
Delegate callbacks from UI code
Handling Timers
Touch Handling
etc... etc...

Resources