Posting NSNotification on the main thread - ios

I found the following code snippet which allows NSNotification to be posted on the main thread from any background thread. I would like to know if this is a safe and acceptable practice please?
dispatch_async(dispatch_get_main_queue(),^{
[[NSNotificationCenter defaultCenter] postNotificationName:#"ImageRetrieved"
object:nil
userInfo:imageDict];
});

Yes you can.
Generally you want the NSNotifications to be sent on the main , especially if they trigger UI activities like dismissing a modal login dialog.
Delivering Notifications To Particular Threads
Regular notification centers deliver notifications on the thread in
which the notification was posted. Distributed notification centers
deliver notifications on the main thread. At times, you may require
notifications to be delivered on a particular thread that is
determined by you instead of the notification center. For example, if
an object running in a background thread is listening for
notifications from the user interface, such as a window closing, you
would like to receive the notifications in the background thread
instead of the main thread. In these cases, you must capture the
notifications as they are delivered on the default thread and redirect
them to the appropriate thread.

Yes
This is - you are getting into the main thread and posting your notification. Can't get any safer than that.

YES
Swift 2 syntax
dispatch_async(dispatch_get_main_queue()) {
NSNotificationCenter.defaultCenter().postNotificationName("updateSpinner", object: nil, userInfo: ["percent":15])
}
Swift 3 syntax
DispatchQueue.main.async {
NotificationCenter.default.post(name: "updateSpinner", object: nil, userInfo: ["percent":15])
}

Somewhere along the line this became possible with:
addObserver(forName:object:queue:using:)
which is here, but the whole point is the queue object.
The operation queue to which block should be added. If you pass nil,
the block is run synchronously on the posting thread.
So how do you get the queue that corresponds to the main runloop?
let mainQueue = OperationQueue.main
Note: this is when you are subscribing to notifications, so you do it once and you're done. Doing it on every single call is terribly redundant.

Related

performFetchWithCompletionHandler does the http fetch must be made in the main thread or in a background thread?

in performFetchWithCompletionHandler, does all http request must be done in the main ui thread or in background thread ?
You should do the HTTP requests in the Background Thread. If you receive some kind of data to populate your dataSource, then you should put the .reloadData() in your Main Thread.
Supposing you're using a tableView to show your results, you may place this code in your completionHandler:
Swift 3:
DispatchQueue.main.async {
tableView.reloadData()
}
This part of the documentation may give you a better explanation about this topic.

Why would you need a DispatchQueue when showing Alerts on Swift?

I am new to Swift, and trying to examine a finished project. But there is something i couldn't understand.
After a network request is completed, the app show an alert under a condition.
func makeNetworkRequest() {
//newtork result...
DispatchQueue.main.async {
self.showAlert(versionMessage: "Error")
}
}
func showAlert(versionMessage: String) {
let alert = UIAlertView(title: "", message: versionMessage, delegate: self)
alert.show()
}
However, it is done with a DispatchQueue. Why would anyone need to use DispatchQueue in this situation.
It’s a conscious design decision from Apple’s side to not have UIKit
be thread-safe. Making it thread-safe wouldn’t buy you much in terms
of performance; it would in fact make many things slower. And the fact
that UIKit is tied to the main thread makes it very easy to write
concurrent programs and use UIKit. All you have to do is make sure
that calls into UIKit are always made on the main thread. So
according to this the fact that UIKit objects must be accessed on
the main thread is a design decision by apple to favor performance.
for more detailed information you can go through this article
https://www.objc.io/issues/2-concurrency/thread-safe-class-design/
In your case , You are showing alert from another thread so you have to write code under the MainThread so , you can get the main thread using below code
DispatchQueue.main.async {
// Your UI Updation here
}
Reason
In Cocoa Touch, the UIApplication i.e. the instance of your application is attached to the main thread because this thread is created by UIApplicatioMain(), the entry point function of Cocoa Touch. It sets up main event loop, including the application’s run loop, and begins processing events. Application's main event loop receives all the UI events i.e. touch, gestures etc.
You´ll for sure notice that the alert will lag if you don´t show the alert on the main thread, that´s because your UI code does always have to be done on your main thread.
So if you're on a background thread and want to execute code on the main thread, you need to call async(). That´s way you call DispatchQueue.main, which is the main thread.

IOS, NotificationQueue call wrapped in DisplaceQueue.global does not fire

I am trying to have my notification targets invoke in the background thread. The documentation asserts the targets will be invoked on the tread where the notification was enqueued.
If I wrap the enqueuing inside the DispatchQueue that runs in the background then the target is never invoked. If I remove the DispatchQueue then the targets are invoked, on the main thread of course.
for aLocation in locations {
// add it to the queue
DispatchQueue.global(qos: DispatchQoS.QoSClass.background).async {
let notify = Notification(name: MyLocationManager.queueName(), object:aLocation)
NotificationQueue.default.enqueue(notify,
postingStyle: NotificationQueue.PostingStyle.asap,
coalesceMask:NotificationQueue.NotificationCoalescing(rawValue: 0),
forModes: nil)
}
}
The short answer would be: NotificationQueues need a run loop
The long version:
The documentation asserts the targets will be invoked on the tread where the notification was enqueued.
This is not entirely true. The documentation asserts this fact only for Notification Centers but not for NotificationQueues
Here's what the NotificationQueue documentation has to say about it:
Whereas a notification center distributes notifications when posted, notifications placed into the queue can be delayed until the end of the current pass through the run loop or until the run loop is idle.
Then you have the enqueue function's 4th parameter:
modes:
The list of modes the notification may be posted in. The notification queue will only post the notification to its notification center if the run loop is in one of the modes provided in the array. May be nil, in which case it defaults to NSDefaultRunLoopMode.
Combine all this with the fact that GCD wont create a run loop on its global concurrent queues, unless somebody explicitly runs one on them, then we have this behaviour where the notification queue does not fire.

mergeChangesFromContextDidSaveNotification Consumes Memory

I have two NSManagedObjectContext, one for ui and one for background tasks. I'm trying to merge changes to UIcontext whenever the background one changed. But whenever I call
mergeChangesFromContextDidSaveNotification:notification
It just start eating memory (will goes up to 1GB on simulator) and cause a crash.
of course I setup a notification:
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(coreUpdateFromApp:)
name:NSManagedObjectContextDidSaveNotification
object:nil];
and also tried doing the merge in main thread, etc. No luck!
I found out that URIRepresentation is causing the issue. For some reason it's keep being called. (by apple's code not mine)
Note that I let it run for under a sec and it uses 64.95MB it will grow pretty fast with same call tree. If I keep it running it would crash the osx as well!
The problem is object:nil. You are listening to an endless echo of notifications.
You need to specify a specific context object to listen to notifications from.
The Problem here is, Since Google Analytics also using core-data we are intercepting the endless notifications fired by Google Analytics as well.
Setting object to non-nil value didn't work for me. Found another way and it worked for me as magic.
Inside your observer I have selector method as below
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(managedObjectContextDidSave(_:)), name: NSManagedObjectContextDidSaveNotification, object: nil)
and,
func managedObjectContextDidSave(notification: NSNotification) {
if notification.object?.persistentStoreCoordinator != self.persistentStoreCoordinator {
return
}
//do remaining task here.
}

iOS: Is [UIApplication schedulelocalnotification] and related local notification manipulating methods thread safe?

My App sometimes need to schedule almost 64 local notifications, which will block my main thread for almost 1 seconde on iPhone4.
I want to do this on a separated thread, is these local notification manipulating methods of UIApplcation thread safe?
dont think so as the docs dont explicitly state it and UIKit in general in large parts isnt thread safe
but it would be worth a try :D the main thread is only a dispatch_async away ;)
--- maybe it would be an option to schedule them individually and run the main loop in between
There are two things in play, thread safety and calling UIKit from background threads. Some UIKit code doesn’t like to be called from a background thread at all and will throw an exception if you attempt to do so (like setting a new content for a UITextView). In other words, there’s something like this in the code:
NSParameterAssert([NSThread isMainThread],
#"This method must be called from the main thread.");
Then comes the thread safety, ie. if the code can be called from a background thread, it might still be written in a way that may result in a bug when you do so:
- (void) doA {
for (id item in allItemsArray) {
// do something
}
}
- (void) doB {
[allItemsArray addObject:#"foo"];
}
Now if one thread calls -doA and another thread calls -doB in the meantime, your app would crash with an exception because you changed the allItemsArray while enumerating it.
So the first question is if the notification methods can be called on a background thread. I’d say they can. In that case you can simply schedule all your notification from a background queue:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i=0; i<64; i++) {
// schedule notification
}
});
You don’t need to care about thread safety, unless there’s another part of your app scheduling other local notifications in the meantime. If there is, you can either create a separate queue to serialize all the notification calling code, or you have to be sure that the methods are thread-safe indeed (in which case I have no authoritative resource to offer).

Resources