UI glitches despite the fact that UI operations run on main thread - swift

I have a bug (which I meet second time already) in our project where I simply add a view at a top of UIViewController's view. Nothing outstanding, something like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.displayEmailNotificationsIfNeeded()
}
The bug is that for some reason Auto Layout works incorrectly and doesn't add it at the top, but around 60pt lower than needed. I suspect that ~60pt comes from manual top constraint adjustment to include navigation bar and status bar, but that's not really important.
The fact is that the problem disappears if I run the method explicitly on the main queue, like this:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("is main thread: ", NSThread.isMainThread())
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("is main thread inside block: ", NSThread.isMainThread())
self.displayEmailNotificationsIfNeeded()
})
}
Print statements return true for both cases. It's not really awful, but just out of curiosity I want to understand what causes this. Is there a way to debug this situation and understand why explicitly performing operations on main thread fixes some UI glitches?

An educated guess - it's not that the displayEmailNotificationsIfNeeded isn't running on the main thread anyway without you adding it to the queue explicitly, it's more a matter of timing. There might be elements moving around as a result of other constraints in your storyboard that are in a different state once viewDidAppear finishes executing. Adding the execution block asynchronously lets viewDidAppear finish (and anything else running synchronously on the main queue) before executing your code.
Hope this helps.

This is not a certain answer as I need to reproduce the bug to be certain but here is what I think might be happening.
When you execute the code directly, layout is not fully ready (it depends on other code you might have) and thus wrong layout. When you explicitly run the code in dispatch_async you are running it on main thread too but on a later point in time and till then layout gets ready and you see correct result.
Also, execute this code and you should be able to understand.
print("1");
dispatch_async(dispatch_get_main_queue(), { () -> Void in
print("2");
})
print("3");

Try running it in viewWillLayoutSubviews(). Your view controllers .view should be fully laid out at this point. Note that this method may be called multiple times (ex: if .view gets resized).

Related

In Swift, if Thread.current.isMainThread == false, then is it safe to DispatchQueue.main.sync recursively once?

In Swift, if Thread.current.isMainThread == false, then is it safe to DispatchQueue.main.sync recursively once?
The reason I ask is that, in my company's app, we had a crash that turned out to be due to some UI method being called from off the main thread, like:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
Since this was getting called from many places, some of which would sometimes call it from a background thread, we were getting crashes here and there.
Fixing all the call sites was not feasible due to the app being over a million lines of code, so my solution to this was simply to check if we're on the main thread, and if not, then redirect the call to the main thread, like so:
public extension UIViewController {
func presentModally(_ viewControllerToPresent: UIViewController, animated flag: Bool, completion: (() -> Void)? = nil) {
guard Thread.current.isMainThread else {
DispatchQueue.main.sync {
presentModally(viewControllerToPresent, animated: flag, completion: completion)
}
return
}
// some code that sets presentation style then:
present(viewControllerToPresent, animated: flag, completion: completion)
}
}
The benefits of this approach seem to be:
Preservation of execution order. If the caller is off the main thread, we'll redirect onto the main thread, then execute the same function before we return -- thus preserving the normal execution order that the would have happened had the original function been called from the main thread, since functions called on the main thread (or any other thread) execute synchronously by default.
Ability to implicitly reference self without compiler warnings. In Xcode 11.4, performing this call synchronously also satisfies the compiler that it's OK to implicitly retain self, since the dispatch context will be entered then exited before the original function call returns -- so we don't get any new compiler warnings from this approach. That's nice and clean.
More focused diffs via less indentation. It avoids wrapping the entire function body in a closure (like you'd normally see done if Dispatch.main.async { ... } was used, where the whole body must now be indented a level deeper, incurring whitespace diffs in your PR that can lead to annoying merge conflicts and make it harder for reviewers to distinguish the salient elements in GitHub's PR diff views).
Meanwhile the alternative, DispatchQueue.main.async, would seem to have the following drawbacks:
Potentially changes expected execution order. The function would return before executing the dispatched closure, which in turn means that self could have deallocated before it runs. That means we'd have to explicitly retain self (or weakify it) to avoid a compiler warning. It also means that, in this example, present(...) would not get called before the function would return to the caller. This could cause the modal to pop-up after some other code subsequent to the call site, leading to unintended behavior.
Requirement of either weakifying or explicitly retaining self. This is not really a drawback but it's not as clean, stylistically, as being able to implicitly retain self.
So the question is: are these assumptions all correct, or am I missing something here?
My colleagues who reviewed the PR seemed to feel that using "DispatchQueue.main.sync" is somehow inherently bad and risky, and could lead to a deadlock. While I realize that using this from the main thread would indeed deadlock, here we explicitly avoid that here using a guard statement to make sure we're NOT on the main thread first.
Despite being presented with all the above rationale, and despite being unable to explain to me how a deadlock could actually happen given that the dispatch only happens if the function gets called off the main thread to begin with, my colleagues still have deep reservations about this pattern, feeling that it could lead to a deadlock or block the UI in unexpected ways.
Are those fears founded? Or is this pattern perfectly safe?
This pattern is definitely not “perfectly” safe. One can easily contrive a deadlock:
let group = DispatchGroup()
DispatchQueue.global().async(group: group) {
self.presentModally(controller, animated: true)
}
group.wait()
Checking that isMainThread is false is insufficient, strictly speaking, to know whether it’s safe to dispatch synchronously to the main thread.
But that’s not the real issue. You obviously have some routine somewhere that thinks it’s running on the main thread, when it’s not. Personally, I’d be worried about what else that code did while operating under this misconception (e.g. unsynchronized model updates, etc.).
Your workaround, rather than fixing the root cause of the problem, is just hiding it. As a general rule, I would not suggest coding around bugs introduced elsewhere in the codebase. You really should just figure out where you’re calling this routine from a background thread and resolve that.
In terms of how to find the problem, hopefully the stack trace associated with the crash will tell you. I’d also suggest adding a breakpoint for the main thread checker by clicking on that little arrow next to it in the scheme settings:
Then exercise the app and if it encounters this issue, it will pause execution at the offending line, which can be very useful in tracking down these issues. That often is much easier than reverse-engineering from the stack trace.
I agree with the comments that you have some structural difficulties with your code.
But there are still times in which I need code to run on the main thread and I don't know if I'm already on the main thread or not. This has occurred often enough that I wrote a ExecuteOnMain() function just for this:
dispatch_queue_t MainSequentialQueue( )
{
static dispatch_queue_t mainQueue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
#if HAS_MAIN_RUNLOOP
// If this process has a main thread run loop, queue sequential tasks to run on the main thread
mainQueue = dispatch_get_main_queue();
#else
// If the process doesn't execute in a run loop, create a sequential utility thread to perform these tasks
mainQueue = dispatch_queue_create("main-sequential",DISPATCH_QUEUE_SERIAL);
#endif
});
return mainQueue;
}
BOOL IsMainQueue( )
{
#if HAS_MAIN_RUNLOOP
// Return YES if this code is already executing on the main thread
return [NSThread isMainThread];
#else
// Return YES if this code is already executing on the sequential queue, NO otherwise
return ( MainSequentialQueue() == dispatch_get_current_queue() );
#endif
}
void DispatchOnMain( dispatch_block_t block )
{
// Shorthand for asynchronously dispatching a block to execute on the main thread
dispatch_async(MainSequentialQueue(),block);
}
void ExecuteOnMain( dispatch_block_t block )
{
// Shorthand for synchronously executing a block on the main thread before returning.
// Unlike dispatch_sync(), this won't deadlock if executed on the main thread.
if (IsMainQueue())
// If this is the main thread, execute the block immediately
block();
else
// If this is not the main thread, queue the block to execute on the main queue and wait for it to finish
dispatch_sync(MainSequentialQueue(),block);
}
A bit late, but I had a need for this type of solution too. I had some common code that could be invoked from both the main thread and background threads, and updated the UI. My solution to the generic use case was:
public extension UIViewController {
func runOnUiThread(closure: #escaping () -> ()) {
if Thread.isMainThread {
closure()
} else {
DispatchQueue.main.sync(execute: closure)
}
}
}
Then to call it from a UIViewController:
runOnUiThread {
code here
}
As others have pointed out, this is not completely safe. You might have some code on background thread that is invoked from the main thread, synchronously. If that background code then calls the code above, it will attempt to run on the main thread and will create a deadlock. The main thread is waiting for the background code to execute, and the background code will wait for the main thread to be free.

AKMetronome countdown, how to publish to main thread from background thread to start recorder?

I've been looking into how to implement a Metronome countdown with the AudioKit lib but ran into an issue related to threads and my implementation.
My implementation uses the AudioKit AKMetronome, a method to handle the metronome.start and the metronome.callback handler to start recording after a given number of bars are completed.
init () {
metronome.callback = handler
}
func record () {
self.metronome.start()
}
The handler computes the metronome position in time and if the desired number of bars completes (countdown), the recorder starts.
Unfortunately, running the .record of AKNodeRecorder in the .callback handler of AKMetronome causes the warning message:
Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.
For that reason, the call to start recording in the metronome.callback handler is passed to the main thread through the GCD API:
DispatchQueue.main.sync {
do {
try self.recorder.record()
} catch {
print("[ERROR] Oops! Failed to record...")
}
}
I've used the .sync blocking method to resolve the computation immediately as possible since
timing is critical in audio applications (the call should be executed in a real-time thread, ideally); Is my understanding that the GCP API main thread provides the highest priority, but I'm not certain if that's the best option for a time-sensitive application?
Would like to get some feedback or direction if possible from other users, thank you!
OK, so the actual question has nothing to do with metronomes or countdowns? What you really want to know is: will using sync from a background thread get me onto the main thread faster?
If so: Basically no. This is not what sync is for or what it means. The async/sync difference has absolutely no effect on speed. If you get onto the main thread using async, you will get on as soon it is free, so you gain nothing whatever by saying sync.
Moreover, if you already arrived into the callback in an async background thread, any damage that can be done is already done; your timing is now inexact and there is absolutely nothing you can do about it unless you happen to have a time machine in your pocket.
Following up on #matt advice regarding calls inside callbacks, the approach I took change based in a .scheduledTimer of Timer.
func record () {
metronome.restart()
Timer.scheduledTimer(withTimeInterval: self.barLength, repeats: false) { _ in
self.startRecording()
print("Timer fired!")
}
}
The record is a handler that initiates the AKMetronome, opted to use .restart so that every time's requested starts from the very start.
A property .barLength holds the value for the computed length of the desired countdown. The .startRecording method that is called, is a handler that takes care of AKRecorder .record call.
Future reader, have in mind that Timer is not meant to be accurate according to (Accuracy of NSTimer) but
unfortunately, this is the best approach I've tested and I found so far.

Dispatch Queues - Nesting, Optimisation & Control

Can I have your advice on the approved approach?
I have four processes that need to run sequentially:
calculateProcess1()
calculateProcess2()
calculateProcess3()
calculateProcess4()
These calculate data and update progress bars (circular) and other screen literals using dispatch queues to make the UI update.
Run them as is and lo and behold they all fire off simultaneously (what a surprise!).
How do I make the top level calls run in sequence (as above) and do I need to make changes to my DispatchQueues in the processes for updating the literals, e.g.:
DispatchQueue.main.sync {
let progressPercentage = (day*100/365)
self.progressLabel.text = String(Int(progressPercentage))+"%"
}
The initiating processes (calculateProcess1-4()) need to run from main. Should this be in ViewDidLoad, a button action or what is the most secure method?
A one approach would be to use Operation and OperationQueue. Setting maxConcurrentOperationCount: Int to 1 on the OperationQueue, will force operations to be performed in a sequence, one after another.
(Previously called NSOperation and NSOperationQueue)
This blogpost can be helpful: https://nshipster.com/nsoperation/
And of course awesome WWDC15 session: Advanced NSOperations
1- Don't sync in main thread as it'll cause a runtime crash , it should be async
DispatchQueue.main.sync {
2-
Should this be in ViewDidLoad, a button action or what is the most secure method?
it's up to you there is no thing related to security here , implement it as your UX
3- to run them in dequence create a custom Queue then dispatch them in it as it' ll run serially if the code inside all these methods run in the same thread of the queue meaning you don't internally dispatch inside another queue , you can also use DispatchGroup to be notified when all are done
If you want to keep it simple you can provide callback blocks to your calculateProcess methods and simply nest them.
calculateProcess1() {
calculateProcess2() {
calculateProcess3() {
calculateProcess4() {}
}
}
}
Should this be in ViewDidLoad, a button action or what is the most
secure method?
viewDidLoad is probably what you want. Keep in mind if you instantiate a new viewController of the same type and show it, it will happen again, once, for that new controller as well. Also, if you are doing anything with view frames, those are not guaranteed to be laid out in viewDidLoad, but if you are simply updating text then it should be fine.

Does defining functions inside viewdidload() save more memory versus defining them outside?

Since viewdidload() is only called once in the lifecycle of that instance of the UIViewController object, does that mean that this example below is a "bad practice" since setBackgroundColor(), a function that is only called once, is unnecesarrily loaded into the memory of the entire class when it really should just exist entirely (defined and called) inside viewdidload()? Or in terms of efficiency, does it not matter where setBackgroundColor() is defined and called?
class MasterViewController: UIViewController {
func setBackgroundColor() {
self.view.backgroundColor = UIColor.green
}
// Do any additional setup after loading the view, typically from a nib.
override func viewDidLoad() {
super.viewDidLoad()
setBackgroundColor()
}
// Dispose of any resources that can be recreated.
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
Making a function local to a method changes its scope, but not the lifetime of code compiled from it. Same goes for methods of the class: their binary codes are not managed individually, at least not at this time. This is not a big deal, though, because executable code of your function is relatively tiny.
What matters here is that the function name is not visible in its outer scope, letting other methods define their own setBackgroundColor() functions unrelated to the one defined within viewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
// Nested function definition
func setBackgroundColor() {
self.view.backgroundColor = UIColor.green
}
// Calling nested function
setBackgroundColor()
}
This improves readability, because function definition is right there at the point where it is used. It also improves maintainability, because whoever is refactoring your code can be certain that there can be no other uses of setBackgroundColor outside of viewDidLoad.
Of course, this is just an example. The nested function is not necessary here - you can rewrite it without setBackgroundColor function, like this:
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.green
}
It's not a case of memory usage, it's a matter of loading the view efficiently.
UIViewControllers control views. But the views isn't created at the same time as the view controller.
Setting the background colour, indeed, any kind of view configuration, in viewDidLoad is done because when that method is called, the view has been created (although not necessarily displayed) at a convenient point in the view controller's life cycle. If you created the view controller and then called your setBackgroundColor method, the self.view part of the call would cause the view to be created straight away if it hadn't already been created.
For certain methods, such as view controller presentations, this allows the creation methods to return as quickly as possible without the view being loaded straight away and keeps the UI responsive.
It's because of this lazy loading of views that UIViewController has the isViewLoaded parameter which returns a Bool for whether the view is loaded into memory or not, but without causing the view to load.
I don't see anything in your code that warrants a memory concern. Micro-optimization is greatest time waster in software development. But since you asked, viewDidLoad is invoked only once. From Apple's Work with View Controllers:
viewDidLoad()—Called when the view controller’s content view (the top of its view hierarchy) is created and loaded from a storyboard. The view controller’s outlets are guaranteed to have valid values by the time this method is called. Use this method to perform any additional setup required by your view controller.
Typically, iOS calls viewDidLoad() only once, when its content view is first created; however, the content view is not necessarily created when the controller is first instantiated. Instead, it is lazily created the first time the system or any code accesses the controller’s view property.
My guess is that any loss in efficiency is worth the gain in having more readable code. In fact, a compiler optimization may even inline the function if you mark it as private. I don't know enough about the Swift compiler (LLVM) to know if this is definitely true but it may be.
Martin Fowler has a great article on function length in which he states that he has many functions that are one line long simply because they make the code easier to understand.
Some people are concerned about short functions because they are worried about the performance cost of a function call. When I was young, that was occasionally a factor, but that's very rare now. Optimizing compilers often work better with shorter functions which can be cached more easily. As ever, the general guidelines on performance optimization are what counts.
Not sure if his notes about function caching apply to Swift, but he also says:
If you have to spend effort into looking at a fragment of code to figure out what it's doing, then you should extract it into a function and name the function after that “what”. That way when you read it again, the purpose of the function leaps right out at you
In general, I wouldn't focus too much on optimization unless you notice that it's a problem. YAGNI
... And as Code Different noted, yes ViewDidLoad() is only called once.

Why does NSCollectionView.makeItem sometimes crash with EXC_BAD_ACCESS?

For some reason, my app just started doing this after months of not doing so. This popped up after a seemingly-unrelated use of OperationQueue (I always use main, so it's done on Thread 1).
As you can see, I call the exact same function 6 times in a row, and only on the 4th time does it fail. I see no pattern...
I haven't done very extensive digging or testing, but it seems a block like this was the culprit:
collectionView.performBatchUpdates({
collectionView.reloadItems()
}, completionHandler: { [weak self] _ in
// cleanup
})
Seems the collection view doesn't like doing much more than insert+remove inside the batch update context. Changing it to this seems to have cured the crash:
collectionView.reloadItems()
// cleanup