A while back I asked similar question here: Difference requiresMainQueueSetup and dispatch_get_main_queue?
I came back to it today and realised I did not grasp it completely yet. When we are writing react native modules for iOS there is dispatch_get_main_queue on ios side and requiresMainQueueSetup needed by react-native.
How and which one of these should be used for following packages and use cases?
asq-react-native-device - package that exports constants like bundle id
asq-react-native-sensors - package that allows to subscribe and receive data from device gyroscope and oher sensors
asq-react-native-google-sign-in - package tat exposes google sign in sdk
For all of these I receive warnings in my dev console i.e.
So I assume requiresMainQueueSetup needs to be used regardless for all packages? I struggle to grasp when it needs to be set to YES or NO however. None of the modules are ui components, google sign in one, however, opens up safari view controller to perform oAuth, does this mean value needs to be set to YES here?
Do I need to use dispatch_get_main_queue whenever I use any of react-native resolver / rejecter bridge methods?
While both methods are related to the way that RN interacts with your module, they are not the same.
+ (BOOL)requiresMainQueueSetup
This method determines if your module will be initialized on the main thread or not; in other words, if your module's constructor (init method) and the constantsToExport method will be called on the main thread.
- (dispatch_queue_t)methodQueue
This method is an override where you can tell RN which dispatch queue to use when calling your module's exported methods (exported using the RCT_EXPORT_METHOD macro). Returning dispatch_get_main_queue() here returns the main queue which belongs to the main thread, asking RN to invoke your exported methods on the main thread, but you can use any other custom dispatch queue if you have such requirement.
Main thread execution
If we're to simplify this, any operation that involves UI manipulation is required to be executed on the main thread or main dispatch queue. Even if your modules are not UI components there are other UI operations that can be executed from native modules, for example: presenting a view-controller (can be a system view-controller or a custom one)
As you see, all the warning that you get originate from the lack of requiresMainQueueSetup implementation, and it tells you the exact reason - a module is overriding the init or the constantsToExport method but doesn't specify if it wishes for it to be executed on the main thread. Up until now, modules that were overriding the init or the constantsToExport were initialized on the main thread by default, assuming that they might need access to UI elements or UIKit. In future RN versions this is going to change - these methods will get called on the main thread only if you explicitly say so.
The packages in question
It doesn't look like you're doing something in your init or constantsToExport that requires the main thread, but at the moment it's as if you are overriding requiresMainQueueSetup and returning YES. I recommend that you do override it and return NO, so you can test that everything works as expected and make sure that you won't have any surprises when RN defaults to this in your case in future RN versions.
Notice that even though these are not UI components, you sometimes do need the main thread when exported methods are called, like in asq-react-native-google-sign-in - the signIn methods are presenting and dismissing view-controllers. In this case you correctly override methodQueue and use the main queue for your exported methods.
Note that it's also possible to keep the method calls in RN background threads by not overriding methodQueue, and dispatch to the main queue yourself when necessary. For example:
dispatch_async(dispatch_get_main_queue(), ^{
[RCTPresentedViewController() dismissViewControllerAnimated:YES completion:nil];
});
Related
So I'm beginning to learn the basics of Grand Central Dispatch and the whole concept of multithreading with iOS applications. Every tutorial will tell you that you must run UI events on the main thread, but I don't completely understand why.
Here's a problem I came across yesterday, and finally fixed it by running a segue on the main thread, but I still don't understand why running it off the main thread was a problem:
I had a custom initial VC (barcode scanner) and a segue to a new view controller with a UIWebView attached. As soon as the VC found a barcode, it called a handler, and in that closure, I had a performSegueWithIdentifier. However, I got a EXC_BAD_ACCESS because of this (it didn't happen when the second VC had a label or a UIImageView, just with UIWebView). I finally realized that for some reason, the closure was called off the main thread, and thus the segue was being performed off the main thread. Why exactly would performing the segue on another thread throw a memory error? Is it because self in self.performSegueWithIdentifier was somehow nil? And why wouldn't Swift automatically dispatch a segue event on the main thread?
Interesting question! The crash isn't related to UIKit. It's a crash specific to UIWebView. Looking at the stack trace, the exception happens in the WebCore::FloatingPointEnvironment::saveMainThreadEnvironment function, which is part of the WebKit init process. Since WebKit manages a threaded execution environment of its own, it makes sense that it needs a definite starting point (i.e. the main thread) to build this environment.
UIKit operations (like presenting a view controller) performed on threads other than main will not cause an exception, but they will be delayed (depending on the QoS of the dispatching queue).
As for why the UIKit operations aren't automatically dispatched on the main queue, I can only speculate that adding extra checks inside the library calls would add too much redundant work that can be avoided simply by following a convention.
For a larger discussion on UIKit and the main thread, see this answer: Why must UIKit operations be performed on the main thread?
The short answer is that all operations that modify the UI of your app need to come together in one place to be evaluated to generate the next frame at regular intervals (the V-Sync interval specifically). Keeping track of all of the mutated state requires all changes to happen synchronously, and for performance reasons, all of these operations are generally batched up and executed once per frame (while also coordinating with the GPU).
Making things thread-safe is difficult, and has major performance implications, all of which can be avoided by requiring access to come from a single (main) thread. You want your UI frameworks to be as fast (i.e. responsive) as possible. If you declare that all your UI objects are main-thread only, then you don't have to slow them down with a bunch of synchronization overhead. I've heard it asked, 'if UIKit is really main-thread only, then why don't they just check the current thread and abort() if it's not the main thread?' and the answer is the same: Even a simple check like that would result in an appreciable performance hit given the number of methods that would need the check and the frequency with which they're called. It's all about speed.
Also, remember, not too long ago, all applications were single threaded.
I am using the following method to browse the photos library in iPhone:
enumerateGroupsWithTypes:usingBlock:failureBlock:
From the API doc and my test, I know the block runs asynchronously. However, my question is: how exactly this block runs asynchronously?
I.e. does it run via the same runloop as the main thread? or via a new & different runloop?
also, does it run in a new & different thread? (assuming 'thread' is not same as 'runloop')
Let's say that I want to do something to update the view(UI) inside the block, particularly do a :
[collectionView reloadData].
How will this "reloadData" message be handled? Will it be handled in the same runloop as the block itself? [Update question: will this "reloadData" be asynchronous or synchronous relative to this block's execution?]
And at last, in case I am asking dumb questions, can anyone suggest a book or other pointers to understand the inner working of asynchronous block in iOS program, e.g. how it's related to runloops and threads?
thanks.
the block is called on the main queue (running on the main thread)
no special runloop mode or so is entered ...
you can see it when you write a simple demo app
either the ALAssetFramework uses GCD dispatch or it registers as a source for runloop events.. but thats an implementation detail
about the update:
a block is a little like a regular function... so reloadData is synchronous normally and it will remain synchronous when used in a block therefore
As I got to know, in iOS main queue is the only candidate of the three (main_dispatch_queue, global_dispatch_queue, serial_dispatch_queue) for executing UI related tasks in GCD. I would like to know why is that so.
This might be a more general question than specific to iOS. But prefer an answer in iOS context.
Found the answer.
In iOS the event loop runs in main thread.
Inside the main method of main.m, UIApplicationMain() sets up the main event loop.
https://developer.apple.com/library/ios/documentation/general/conceptual/Devpedia-CocoaApp/MainEventLoop.html
I have an Android application using MvvmCross. The App launches via an MvxSplashScreenActivity as the main launcher and I provided a setup class deriving from MvxAndroidSetup.
It seems however that my MvxAndroidSetup.CreateApp() override is called on a ThreadPool thread (see https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Droid/Views/MvxBaseSplashScreenActivity.cs#L79).
What is the best way to ensure certain parts of my App Initialization are performed on the MainThread?
Most of the modern platforms - WindowsStore, WindowsPhone and iOS - allow you to bundle a static Default.jpg (or similar) to entertain your users while your app starts.
Android doesn't do this - it simply launches your app using the Activity marked as the MainLauncher.
If your app needs to do some initialization work (as most MvvmCross apps do) then this leaves you with a choice - do you do that work on the UI thread (which then results in an unresponsive UI) or do you display a placeholder SplashScreen and then do the initialization work on the background thread.
This is what MvvmCross tries to allow you to do
it provides you with a splashscreen class
it overrides the OnCreate of the splashscreen to do the minimal work it can on the UI thread during the splashscreen OnCreate (during this time the UI is black which is horrible)
then it does the bulk of init on a threadpool thread - as #CheeseBaron has pointed out on MvxBaseSplashScreenActivity.cs#L79
The bulk of initialisation - loading types, starting services, recovering settings, loading language files, etc - shouldn't need to be done on the UI thread.
If there is some part of initialization that you need to do on the UI thread, then it's up to your app to work out how and when to marshal that work back to the UI - e.g.
by using MvxMainThreadDispatcher.cs
by deferring that task until a later time (e.g. using Lazy IoC which will be triggered by viewmodel creation)
by overriding the SplashScreen's OnInitialisationComplete method
However, obviously at no point should you try to marshal any long-lasting work onto the UI thread... the UI thread is for the UI, not for heavy calculation or for any blocking work.
You should aim to keep the UI responsive, even during startup.
Detailed note:
The above description covers the 'normal launch' of an app from e.g. the android Home page.
However, if you dive deeper, then this is not the only way an app can be started - it can also be started from push notifications, from recovering from being killed ("tombstoned" in WP speak) or from things like broadcast receivers.
In those situations, the MvvmCross app initialisation may occur in other ways than described above:
in the case of an MvxActivity being directly launched (e.g from push or after recovering from tombstoning), then the whole of Setup is currently run on the UI thread during the OnCreate of that activity - this isn't ideal and is something I would hope the framework can improve on in the future.
in the case of a background service starting, it's up to the app engineer to work out what to setup when - e.g. see Using MvvmCross from content providers and activities
One opportunity for an alternative startup route is to subclass the Android Application object - see http://developer.android.com/reference/android/app/Application.html
On Cocoa Touch, if we update UI elements from a background thread, bad stuff happens.
Problem is, it doesn't happen all the time, so some mild background UI meddling might go unnoticed for a while until it kicks your right in the teeth.
Is there a way to make UIKit run in a pedantic mode so that as soon as someone updates an element from a background thread it crashes or logs something to the console?
You can check yourself if you are doing stuff on the main thread or not before doing your UI updates. I've written myself the following macros:
/// Stick this in code you want to assert if run on the main UI thread.
#define DONT_BLOCK_UI() \
NSAssert(![NSThread isMainThread], #"Don't block the UI thread please!")
/// Stick this in code you want to assert if run on a background thread.
#define BLOCK_UI() \
NSAssert([NSThread isMainThread], #"You aren't running in the UI thread!")
I tend to group my code in methods where method A does some processing and then calls method B, which does the UI updates. At the beginning of method B I stick the BLOCK_UI() macro, which will assert if it is not being run on the UI. Also, for the long running tasks I use the other macro. I've put these macros and more random stuff at https://github.com/gradha/ELHASO-iOS-snippets which you may find useful.
These macros require discipline in using them, unfortunately. A more intrusive way to deal with such situations could be to wrap all the SDK interface objects through a proxy (maybe swizzling at launch?) which asserted if they were used not in the main thread. These proxying/swizzling would happen only in debug builds or simulator environment, to avoid bogging down real releases. I've considered doing this... but looks like a pain to do properly.