Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warn on calls to UIKit from background threads

Tags:

ios

uikit

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]

like image 947
Amos Joshua Avatar asked Jun 11 '12 08:06

Amos Joshua


People also ask

How we can ensure execute code in the main thread?

If you're on a background thread and want to execute code on the main thread, you need to call async() again. This time, however, you do it on DispatchQueue. main , which is the main thread, rather than one of the global quality of service queues.

Why is UIKit not thread safe?

As we can see, most of the components in UIKit is described as nonatomic `, this means there are not thread safe. And it is unrealistic to design all the properties as thread-safe in UIKit because it is such a huge framework.

What is main thread in Swift?

The main thread is the one that starts our program, and it's also the one where all our UI work must happen. However, there is also a main queue, and although sometimes we use the terms “main thread” and “main queue” interchangeably, they aren't quite the same thing.


2 Answers

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.

like image 64
Andrew Ebling Avatar answered Oct 15 '22 09:10

Andrew Ebling


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];
like image 26
Imran Avatar answered Oct 15 '22 07:10

Imran