Warn on calls to UIKit from background threads - ios

iOS's UIKit is not thread-safe, let us call this fact well known. I know the rule, I'm careful, but I still get bitten - and every now and then the resulting crash is far enough removed from the offending background call into UIKit to make tracking down the problem a less than joyeus experience.
This problem seems like it could be easy to solve - have UIKit classes/methods warn when they are invoked from a background thread, at least as a debug feature. As far as I'm aware, iOS does not provide any such feature. Of course one could achieve the same effect manually by having some form of assertions precede such calls, but this solution is not the most elegant and in addition suffers from the same weakness as the original problem, namely that programmers are prone to forgetfulness.
Does anyone have a more elegant solution? How do you deal with this problem in your projects?
(Note: this question is related, but not quite as explicit. One is left wondering)
UPDATE: Andrew's answer is the solution I was looking for at the time, however note that at least as of Xcode 9 this is now provided by xcode/ios. For instance, adding this code:
DispatchQueue.global().async {
print(self.view.frame)
}
To a UIView's viewDidLoad method produces a runtime warning inline in Xcode UIView.frame must be used from the main thread only and a message printed to the console: Main Thread Checker: UI API called on a background thread: -[UIView frame]

This code (just add to project and compile this file without ARC) causes assertions on UIKit access outside of the main thread: https://gist.github.com/steipete/5664345
I've just used it to pickup numerous UIKit/main thread issues in some code I've just picked up.

I try not to introduce multi threading unless I have tried a single threaded approach first but it depends on the problem you are trying to solve.
Even when multithreading is the only option I usually avoid long running background operations or operations that that perform several unrelated tasks.
Just my opinion.
Edit
An example of doing work on the main thread whilst displaying a loading spinner:
MBProgressHUD *hud = [MBProgressHUD customProgressHUDInView:view dim:dim];
[hud show:NO];
//Queue it so the ui has time to show the loading screen before the op starts
NSBlockOperation *blockOp = [NSBlockOperation blockOperationWithBlock:block];
NSBlockOperation *finOp = [NSBlockOperation blockOperationWithBlock:^{
[MBProgressHUD hideAllHUDsForView:view animated:NO];
}];
[finOp addDependency:blockOp];
[[NSOperationQueue mainQueue] addOperations:#[blockOp, finOp] waitUntilFinished:NO];

Personally anytime I open the box to a multithreaded approach I start wrapping ALL interface based calls in performSelectorOnMainThread: so that there is never an issue. If we've already made it to the main there this call shouldn't cause any significant slow down, but if its called from a background thread I can rest easy knowing its safe.

Related

What is the default thread

In iOS, we have GCD and Operation to handle concurrent programming.
looking into GCD we have QoS classes, and they're simple and straight forward, this question is about why DispatchQueue.main.async is commonly used to asynchronies X tasks in the Main Thread.
So when we usually handle updating something in the UI we usually use that function since to prevent any irresponsiveness from the application.
makes me think is writing code inside the UIViewController usually executed in the main thread ?
but also knowing that callback & completionHandler usually execute without specifying on what thread they are in, and the UI never had a problem with that !! so it is on the background ?
How Swift handles this ? and what thread am i writing on by default without specifying anything ?
Since there are more than one question here, let's attempt to answer them one by one.
why DispatchQueue.main.async is commonly used to asynchronies X tasks
in the Main Thread.
Before mentioning a direct answer, make sure that you don't have confusion of understanding:
Serial <===> Concurrent.
Sync <===> Async.
Keep in mind that DispatchQueue.main is serial queue. Using sync or async has nothing to do with determining serialization or currency of a queue, instead they refer to how the task is handled. Thus saying DispatchQueue.main.async means that:
returns control to the current queue right after task has been sent to
be performed on the different queue. It doesn't wait until the task is
finished. It doesn't block the queue.
cited from: https://stackoverflow.com/a/44324968/5501940 (I'd recommend to check it.)
In other words, async means: this will happen on the main thead and update it when it is finished. That's what makes what you said:
So when we usually handle updating something in the UI we usually use
that function since to prevent any irresponsiveness from the
application.
seems to be sensible; Using sync -instead of async- will block the main.
makes me think is writing code inside the UIViewController usually
executed in the main thread ?
First of all: By default, without specifying which thread should execute a chunk of code it would be the main thread. However your question seems to be unspecific because inside a UIViewController we can call functionalities that are not executed on the main thread by specifying it.
but also knowing that callback & completionHandler usually execute
without specifying on what thread they are in, and the UI never had a
problem with that !! so it is on the background ?
"knowing that callback & completionHandler usually execute without specifying on what thread they are in" No! You have to specify it. A good real example for it, actually that's how Main Thread Checker works.
I believe that there is something you are missing here, when dealing when a built-in method from the UIKit -for instance- that returns a completion handler, we can't see that it contains something like DispatchQueue.main.async when calling the completion handler; So, if you didn't execute the code inside its completion handler inside DispatchQueue.main.async so we should assume that it handles it for you! It doesn't mean that it is not implemented somewhere.
Another real-world example, Alamofire! When calling
Alamofire.request("https://httpbin.org/get").responseJSON { response in
// what is going on here work has to be async on the main thread
}
That's why you can call it without facing any "hanging" issue on the main thread; It doesn't mean its not handled, instead it means they handle it for you so you don't have to worry about it.

Main Thread Checker: UI API called on a background thread: -[UIApplication delegate] Freezes the application

Whenever my application fetch data from JSON it sometimes get this warning and application freezes right away:
Main Thread Checker: UI API called on a background thread: -[UIApplication delegate]
PID: 7439, TID: 362794, Thread name: (none), Queue name: NSOperationQueue 0x60000002f7c0 (QOS: UNSPECIFIED), QoS: 0
Backtrace:
Can anyone explain how to get rid of it?
It looks like you're updating something on the UI from a thread that's not the main thread.
There's not a lot to go on in your question. But if you're using JSON, you're probably retrieving it asynchronously. Make sure that whenever you update the UI from something retrieved, that you wrap it up in a call to the main thread through something like
dispatch_async(dispatch_get_main_queue(), ^{
// code to post to UI
});
In the latest versions of swift / Xcode / simulator even examining a UI control for a value can throw this.
There are a ton of questions and resources on this very topic. For instance:
Thorough Tutorial
SO Answer
But here's what I guess you want:
DispatchQueue.main.async {
// Access UI stuff here
}
When ever my application fetch data from json
So you should start with the code that fetches that data. Somewhere you are probably calling [[UIApplication sharedApplication] delegate] on a background thread. That's not allowed.
This likely means you're using your application delegate as a place to store model data. You shouldn't do that. There's almost nowhere in an app that you should reference the application delegate. (This is a very common mistake because it's sometimes done in sample code for simplicity.)
If the program crashes, the location should be in the stacktrace, so I would start by looking there, but otherwise, you'll need to audit your code (likely around JSON parsing or network requests) to find where you're doing this.
As noted in the comments below, there is no quick fix to this, and you have almost certainly made your code less stable and more likely to crash in the field. That said, creating a singleton to hold global values in Swift looks like this:
class SomeSingleton {
static let shared = SomeSingleton()
// Properties you want to be available via the singleton
}
You access it with:
SomeSingleton.shared.<property>
This does not make anything thread-safe, but if the singleton's properties are immutable (let), then fetching them via SomeSingleton.shared can be safely called on any thread, unlike UIApplication.shared.delegate.
Again, it sounds like you have significant concurrency issues in this code, and this is not a quick fix; it is just one tool that is often used rather than putting random values on the AppDelegate.

Instruments blocks Background Thread?

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];
}

How to find the state of threads in iOS?

I have implemented some threading functions (I used NSInvocaionOperation) that does some background process without interfering the UI response. Everything works well and all the selectors that I added in operation queue are being called. (I'm saying this by putting NSLog within all selector methods).
I want to know some more details to check/examine the state of each threads.
That is,
Can I measure the time taken by the selectors to complete?
Can I know the current state of the particular thread?
It will be very helpful if you added some more points about thread optimization.
Thanks in advance.
You can get thread execution status by invoking the methods like,
– isExecuting
– isFinished
– isCancelled which returns bool.
also have a look at this
Currently the best way to do thread optimization on iOS is to forget about explicit thread management and use Grand Central Dispatch instead. This doesn’t apply in all cases, but if you just call some selectors in background as you write, explicit threading is clearly overkill.
This is how you spin a selector into background with GCD:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self doSomeLongerTask]; // runs in a background thread
dispatch_async(dispatch_get_main_queue(), ^{
[self reportOperationDone]; // runs on the UI thread
});
});

Schedule NSStreams on secondary thread

In an iPad app I'm developing, I need to put the network handling on a separate thread since it occasionally blocks the UI of the app. At the moment, I have created a Connection object in which all the networking logic goes (NSStreams and its delegate methods).
The main obstacle is how to create the secondary thread and schedule the NSStreams on the run loop of this thread. Do I explicitly create an NSThread that is then owned by the Connection object?
I have been experimenting with NSOperation, but this didn't seem like the best solution as I feel the need for a thread dedicated to handling networking events.
Pointers and advice are welcome. Any sample code might be helpful as well.
Bart
I like the detachNewThreadSelector... approach too, but FYI you can use NSOperation and NSOperationQueue. It'll throw non-concurrent operations onto separate threads.
To get the streams going, you're looking at this kind of thing:
[self.outputStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:mode];
Definitely look at the Apple sample "PictureSharing" at http://developer.apple.com/library/mac/#samplecode/PictureSharing.
In particular, copy the FileSendOperation and FileReceiveOperation classes, and the QRunLoopOperation. I also use the LinkedImageFetcher sample's QWatchedOperationQueue class, which works well with the PictureSharing classes. I took their *SendOperation and *ReceiveOperation classes and turned them into classes sending/receiving what I needed (some NSData).
Then it's as easy as:
FileSendOperation *op;
op = [[[FileSendOperation alloc] initWithFilePath:somePath outputStream:outStream ] autorelease];
[self.queue addOperation:op finishedAction:#selector(networkingDone:)];
I did just some googling, and I came up with this:
http://kdl.nobugware.com/post/2008/12/22/nsthread-iphone-template/
I think this is what you need ;)
EDIT:
http://www.xprogress.com/post-36-threading-tutorial-using-nsthread-in-iphone-sdk-objective-c/
Maybe that is usefull to...
If you read the code, you see performSelectorOnMainThread (or something) so you can send back info from thread to thread.

Resources