I have a game that I'm getting ready to submit. I've tested it a lot and it seems to be solid -- no crashes and no unexpected behavior. I am not forcing anything to run on the main thread (except for one place where I followed a sample in a book) and now I'm concerned that under some circumstance, on somebody else's device, the game will not work right or crash because I'm not doing something on the main thread that should be on the main thread. But I don't understand what needs to be run on the main thread. I googled for a checklist or summary of what methods need run on the main thread and didn't find anything useful. Is this something I need to worry about? The app is for iOS 5.1 and up and it uses in-app purchases and Game Center. I use the cocos2d framework and also a lot of UIKit stuff.
most methods running on the main thread are fine,
especially the UI,
you want to use the back thread for processes that can take long, like http requests, database or data sorting, or any other process that can pause the user experience while is processing.
if you load lots of objects you can produce a memory warning that can result in a crash,
so you can release non needed objects when you get a memory release.
good luck
Related
First off, I'd like to clarify that I'm not talking about concurrency here. I fully understand that having multiple threads modify the UI at the same time is bad, can give race conditions, deadlocks, bugs etc, but that's separate to my question.
I'd like to know why MacOS/iOS forces the main thread (ID 0, first thread, whatever) to be the thread on which the GUI must be used/updated/created on.
see here, related:
on OSX/iOS the GUI must always be updated from the main thread, end of story.
I understand that you only ever want a single thread doing the acutal updating of the GUI, but why does that thread have to be ID 0?
(this is background info, TLDR below)
In my case, I'm making a rust app that uses a couple of threads to do things:
engine - does processing and calculations
ui - self explanatory
program/main - monitors other threads and generally synchronizes things
I'm currently doing something semi-unsafe and creating the UI on it's own thread, which works since I'm on windows, but the API is explicitly marked as BAD to use, and it's not cross compatible for MacOS/iOS for obvious reasons (and I want it to be as compatible as possible).
With the UI/engine threads (there may be more in the future), they are semi-unstable and could crash/exit early, outside of my control (external code). This has happened before, and so I want to have a graceful shutdown if anything goes wrong, hence the 'main' thread monitoring (among other things it does).
I am aware that I could just make Thread 0 the UI thread and move the program to another thread, but the app will immediately quit when the main thread quits, which means if the UI crashes the whole things just aborts (and I don't want this). Essentially, I need my main function on the main thread, since I know it won't suddenly exit and abort the whole app abruptly.
TL;DR
Overall, I'd like to know three things
Why does MacOS/iOS enforce the GUI being on THread 0 (ignoring thread-safety outlined above)
Are there any ways to bypass this (use a different thread for GUI), or will I simply need to sacrifice those platforms (and possible others I'm unaware of)?
Would it be possible to do something like have the UI run as a separate process, and have it share some memory/communicate with the main process, using safe, simple rust?
p.s. I'm aware of this question, it's relevant but doesn't really answer my questions.
Why does MacOS/iOS enforce the GUI being on Thread 0.
Because it's been that way for over 30 years now (since NeXTSTEP), and changing it would break just about every program out there, since almost every Cocoa app assumes this, and relies on it regularly, not just for the main thread, but also the main runloop, the main dispatch group, and now the main actor. External UI events (which come from other processes like the window manager) are delivered on thread 0. NSDistributedNotifications are delivered on thread 0. Signal handling, the list goes on. Yes, it is certainly possible for Darwin (which underlies Cocoa) to be rewritten to allow this. That's not going to happen. I'm not sure what other answer you want.
Would it be possible to do something like have the UI run as a separate process, and have it share some memory/communicate with the main process, using safe, simple rust?
Absolutely. See XPC, which is explicitly for this purpose (communicating, not sharing memory; don't share memory, that's a mess). See sys-xpc for the Rust interface.
I have sometimes app freezing I do not know what is the source of problem.
It happens rarely but happens. I consider how to track such issue
I have crashlytics from firebase but there is no errors as freezing is I suppose something with threads maybe some deadlocks or infinite loops.
Is there way to track such things?
What maybe reasons of app freezing and being inactive (it is SwiftUI app) ?
Are there possibilities to track/profile apps installed on devices (not connected via Xcode) and send informations such that are available in Instrument to something like firebase console or other 3rd party tools.
Update
I think I do not indicated important fact.
It freezes UI definitely not for a few seconds but app need do be killed and restarted again.
There might be some heavy process that locks your main thread, so it freezes your UI.
You track the problems, performance, leaks and more using Instruments.
There are sample topics:
Instruments Tutorial with Swift: Getting Started
How to find and fix slow code using Instruments
And WWDC videos like
Using Time Profiler in Instruments
Creating Custom Instruments
I have experienced some of these weird behaviours before. And mostly the reason behind it was a task that is blocking the main thread making it unresponsive. Pause the app and check the stack trace. Also make sure all your UI operations are done on the main thread. Hope this helps!
Why is it the responsibility of the programmer to call UI related methods on the main thread with:
DispatchQueue.main.async {}
Theoretically, couldn’t this be left up to the compiler or some other agent to determine?
The actual answer is developer inertia and grandfathering.
The Cocoa UI API is huge—nay, gigantic. It has also been in continuous development since the 1990's.
Back when I was a youth and there were no multi-core, 64-bit, anything, 99.999% of all applications ran on the main thread. Period. (The original Mac OS, pre-OS X, didn't even have threads.)
Later, a few specialized tasks could be run on background threads, but largely apps still ran on the main thread.
Fast forward to today where it's trivial to dispatch thousands of tasks for background execution and CPUs can run 30 or more current threads, it's easy to say "hey, why doesn't the compiler/API/OS handle this main-thread thing for me?" But what's nigh on impossible is re-engineering four decades of Cocoa code and apps to make that work.
There are—I'm going to say—hundreds of millions of lines of code that all assume UI calls are executing concurrently on the main thread. As others have pointed out, there is no cleaver switch or pre-processor that's going to undo all of those assumptions, fix all of those potential deadlocks, etc.
(Heck, if the compiler could figure this kind of stuff out we wouldn't even have to write multi-threaded code; you'd just let the compiler slice up your code so it runs concurrently.)
Finally, such a change just isn't worth the effort. I do Cocoa development full time and the number of times I have to deal with the "update control from a background thread problem" occurs, at most, once a week or so. There's no development cost-benefit analysis that's going to dedicate a million man-hours to solving a problem that already has a straight forward solution.
Now if you were developing a new, modern, UI API from scratch, you'd simply make the entire UI framework thread safe and whole question goes away. And maybe Apple has a brand new, redesigned-from-the-ground-up, UI framework in a lab somewhere that does that. But that's the only way I see something like this happening.
You would be substituting one kind of frustration for another.
Suppose that all UI-related methods that require invocation on the main thread did so by:
using DispatchQueue.main.async: You would be hiding asynchronous behaviour, with no obvious way to "follow up" on the result. Code like this would now fail:
label.text = "new value"
assert(label.text == "new value")
You would have thought that the property text just harmlessly assigned some value. In fact, it enqueued a work item to asynchronously execute on the main thread. In doing so, you've broken the expectation that your system has reached its desired state by the time you've completed that line.
using DispatchQueue.main.sync: You would be hiding a potential for deadlock. Synchronous code on the main queue can be very dangerous, because it's easy to unintentionally block (on the main thread) yourself waiting for such work, causing deadlock.
I think one way this could have been achieved is by having a hidden thread dedicated to UI. All UI-related APIs would switch to that thread to do their work. Though I don't know how expensive that would be (each switch to that thread is probably no faster than waiting on a lock), and I could imagine there's lots of "fun" ways that'll get you to write deadlocking code.
Only on rare instances would the UI call anything in the main thread, except for user login timeouts for security. Most UI related methods for any particular window are called within the thread that was started when the window was initialized.
I would rather manage my UI calls instead of the compiler because as a developer, I want control and do not want to rely on third party 'black boxes'.
check https://developer.apple.com/documentation/code_diagnostics/main_thread_checker
and UPDATE UI FROM MAIN THREAD ONLY!!!
If I write accidentally UI updating code on a background thread after fetching data from network req. So Will my application get crash?
From apple docs:
Updating UI on a thread other than the main thread is a common mistake that can result in missed UI updates, visual defects, data corruptions, and crashes. source
So it can crash, but it can also not crash. It might update the UI or it might not. It might crash immediately when the code is called; it might crash in the next runloop, or it might crash minutes later. It might cause weird UI that make you say WTF. In short, its behavior is undefined. Which makes can make it a really hard bug to track down and fix.
If you are asking what is the exact behavior to expect when doing this. The answer is: in a debug environment you should expect the Main Thread Checker to catch it and cause a crash with a good crash report. If it is a production build you can expect some crashes that look like this: Application crashes very rarely with UI update on secondary thread, but they might look different. If you have a small user base you might not see any crashes, but still have a very buggy app.
Straight Answer : Your Application Won't crash.
UI Update must be done in Main thread
Apple Documentation:
DispatchQueue manages the execution of work items. Each work item
submitted to a queue is processed on a pool of threads managed by the
system.
So, use
DispatchQueue.main.async {
//your UI code
}
Why do you want to update UI in the background thread ? it's a bad practice.
You'll get something like this:
I have an iPhone app which pretty much is a mobile app for a website. Pretty much everything it does is call API methods from our server. The app retrieves the user's information, and keeps updating the server using the API.
My colleague and I had a discussion whether to introduce GCD to the downloading aspect on the app. My colleague argues that since the UI needs to wait for the download to complete before it can display the pictures, text or whatever, there is absolutely no need for GCD. My argument is that we should keep the main thread busy with UI rendering (even if there is no data), and introduce GCD to the app to create other threads for download.
Which argument is right here? In my case, if the UI renders with no data, will there be some sort of lag? Which will yield a cleaner, sleeker and faster app?
One argument would be : what will happen when the download fails and times out because there is a problem at the server end ?
Without GCD the app will remain blocked and will crash after a time
out since the UI can not be blocked for longer than 20 seconds.
With GCD the application remains functional but there will be no data
being downloaded and the application will not crash.
Other aspects to take into account are :
the thread safety of the objects that you are using
how you handle downloads that are no longer necessary because the user navigates away from the page
I don't think doing time consuming operations in the main thread is a good idea.
Even if user have to wait for the data te be downloaded before he can do anything meaningful, still he will not hope UI is blocked.
Let's assume you have a navigator view, and after user tap some button, you push a new view to it and start download something. If user suddenly decides he don't want to wait anymore, he tap the "back" button. If your downloading operation blocks UI, user will have to wait it to end, it's really bad.
A more appropriate question would perhaps be if you should download asynchronously or on the main thread for your app, since there are several different methods to download asynchronously on iOS (e.g. using NSThread, NSOperation or indeed GCD). An easy approach to achieve your goals could be to use the AFNetworking library. It makes multithreaded networking / internet code very easy to implement and understand.
Personally I'm very fond of GCD and recommend you learn it someday soon, though in itself it is not as suitable for asynchronous downloading compared to a library like AFNetworking (that uses GCD under the hood, I believe).
Here is a good read on using NSOperationQueues (that uses GCD behind the scenes) to download images. There is also some Github code you can check out. They have an elegant solution to pause downloads and enqueue new downloads when the user moves to different parts of your app.
http://eng.alphonsolabs.com/concurrent-downloads-using-nsoperationqueues/?utm_medium=referral&utm_source=pulsenews
Use GCD / NSOperationQueues as opposed to using NSThreads. You will have a good learning on core fundamentals and at the same time create a well architectured app. :)