how to receive NSWorkspace and accessibility notifications on another thread - objective-c

I am trying to do window management, but I need the code running on a separate thread.
The first thing I need to do is subscribe to app notifications like this:
NSNotificationCenter *nc = [[NSWorkspace sharedWorkspace] notificationCenter];
NSString *not = NSWorkspaceDidLaunchApplicationNotification;
[nc addObserver:self selector:#selector(appLaunched:) name:not object:nil];
But if I simply call addObserver on another thread, will the notifications be delivered there instead?
Apple has this reference, but it seems overcomplicated:
https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Notifications/Articles/Threading.html
If the answer to the first question is no, then why couldn't I just forward the message like this?
NSThread *other;
- (void)appLaunched:(NSNotification*)not {
if([NSThread currentThread] != otherThread)
[self performSelector:#selector(appLaunched:) onThread:other withObject:not waitUntilDone:NO];
else
// do respond to notification
}
The second thing I need to do is add an AXObserver to a runloop on the other thread.
If I call CFRunLoopGetCurrent() from another thread, will a run loop automatically be created like calling [NSRunLoop currentRunLoop] or do a I have to create one?

Observers which are registered using -addObserver:selector:name:object: receive the notification on the thread where it's posted, not where they registered. There's also -addObserverForName:object:queue:usingBlock:, which causes the notification to be received on the specified queue, but that doesn't let you make it arrive on a specified background thread. (Only the main queue is tied to a thread.)
You can shunt a notification to another thread in the manner you suggest. However, the original receiving thread has to be idling to receive the notification in the first place. Or, rather, it has to be idling in order to allow NSWorkspace to detect the condition which causes it to post the notification.
All threads create a runloop for themselves as soon as it's requested. It's basically impossible to observe a thread not having a runloop, so you might as well just act as though the runloop is created when the thread is created.
All of that said, your original goal – "I am trying to do window management, but I need the code running on a separate thread" – is problematic. Many GUI manipulations are not legal from background threads. Also, why do you "need" to do it from a background thread? And if your main thread is not free, you're not going to receive the workspace notifications in the first place.

Related

NSNotificationCenter removeObserver: in dealloc and thread-safety

I'm using ARC and I'm calling [[NSNotificationCenter defaultCenter] removeObserver:someObserver]; in observer's dealloc.
From NSNotificationCenter Class Reference
Be sure to invoke this method (or removeObserver:name:object:) before
notificationObserver or any object specified in
addObserver:selector:name:object: is deallocated.
NSNotificationCenter does not retain the observer.
Q1: Is NSNotificationCenter thread-safe?
In case, the observer is being deallocated(and removing observer from the notification center) and another thread post a notification at the same time.
I encounter random crash and I suspect this is the case.
Q2: Is this situation possible?
Q3: Does it lead to EXC_BAD_ACCESS?
Q4: Then, is it safe to call [[NSNotificationCenter defaultCenter] removeObserver:someObserver]; in observer's dealloc?
Q5: If it is not safe, where should I call removeObserver:?
I just stumbled into this problem myself: I had one notification just in the process of being sent (which always happens in the main thread) while the object was in the process of being deallocated from a background thread. I fixed it by simply performing removeObserver in the main thread and waiting:
- (void)removeNotificationCenterObserver
{
NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
[notificationCenter removeObserver:self];
}
- (void)dealloc
{
[self performSelectorOnMainThread:#selector(removeNotificationCenterObserver) withObject:self waitUntilDone:YES];
}
This waits until the current run loop cycle ends and executes this message at the beginning of the next run loop cycle. This ensures that any functions that are still running will finish.
Yes, NSNotificationCenter doesn't retain observer, but it still has a pointer to it in it's dispatch table.
Q1: Quoting Apple docs
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.
Q2,3: Yes.
Q4,5: AFAIK it's safe unless you stumble into circular reference.
I usually add/remove in -viewWillAppear:/-viewWillDisappear: for UIViewControllers and -init/dealloc for other classes.
I've wondered the same thing, and I can't find it documented. Here's what I think is going on.
removeObserver: is not thread safe in the way that you want it to be.
Think about the following situation. The last reference to the observer is released while executing code on thread A. Thread A will call the observer's dealloc method. At the same time, the observed object executes a [NSNotificcationCenter postNotificationName:object:] from thread B. This leads to an unavoidable race condition. That is, a notification will be in flight while your object is within its dealloc method.
- (void)init {
...
[[NSNotificcationCenter defaultCenter] addObserver:self
selector:#selector(callback:)
name:#"whatever"
object:nil];
...
}
- (void)dealloc {
// If the observed object posts the notification on thread B while
// thread A is here, there's a race! At best, thread B will be
// in callback: while thread A is here in dealloc. That's probably
// not what you expect. The worst case is that thread B won't make
// make it to callback: until after thread A completes the dealloc
// and the memory has been freed. Likely crash!
[[NSNotificationCenter defaultCenter] removeObserver:self];
// If the observed object does the post when thread A is here,
// you'll be fine since the observation has been removed.
}
This isn't a problem for main thread objects that are only observing other main thread objects since, by definition, you can't get into the thread A and B scenario I described.
For multi-threaded cases, the only way to guarantee you'll avoid the problem is to ensure that the observation stops before the observer's refcount hits 0. If someone else is responsible for the observer's lifetime (i.e. you have any sort of term or close method), it's easy. If not, I don't know of a solution.

Better way to Trigger Asynchronous Callbacks in Objective-C

I am looking for a better way to do this, if possible.
I have an asynchronous callback that updates a local sqlite database. I set a flag in a singleton variable (archiveUpdateComplete) when the update completes. I sleep in a do-while until the flag gets set to true, then I hydrate my tableview. Would like to remove sleep()! Thanks for any suggestions.
#define kBgQueue dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0)
- (void)viewDidLoad
{
dispatch_async(kBgQueue, ^{
//Hydrate word archive table view
do {
sleep(1.0);
} while ([sharedManager archiveUpdateComplete]==NO);
[self performSelectorOnMainThread:#selector(hydrateWordArchive) withObject:nil waitUntilDone:YES];
//Run custom activity indicator
dispatch_async(dispatch_get_main_queue(), ^{
[MBProgressHUD hideHUDForView:self.view animated:YES];
});
});
}
If you need to poll
Polling/sleeping is rarely necessary or good. As an alternative:
You can attach an NSTimer to the main thread's run loop.
The selector the timer calls can test [sharedManager archiveUpdateComplete]
if YES is returned, then
invalidate the timer
call [MBProgressHUD hideHUDForView:self.view animated:YES];
If you don't need to poll
There are a few immediate alternatives. Which you choose depends on what knows about what:
If your manager knows who to message following completion, then the manager can simply message it. If that must occur on the main thread you can use -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] to forward to the main thread. You may also see this approach with delegates. In the case of a singleton, it doesn't make a lot of sense to take this route.
If your manager does not know who is interested in the change/completion, your manager can post a NSNotification after the task has finished (on the current thread or from the main thread).
Key Value Observing (KVO) is another option.
Perhaps I'm missing something, but why don't you just use a completion callback for this?
In other words, you change your computation to "think" in terms of nested blocks. The first async block (on some concurrent queue) does the work of updating the database, and when it's done it dispatches another async block (to the same concurrent queue) which hydrates the tableview. Finally, from that block you dispatch_async yet another block on the main queue which updates the UI, since that's the only bit that needs to execute on the main queue.
Rather than poll, in other words, you want to chain your async operations. See COMPLETION CALLBACKS section of the man page for dispatch_async().

Is dispatch_async(dispatch_get_main_queue(), ...) necessary in this case?

I came across this piece of code, and I can't quite figure out why the author did this. Take a look at this code:
someMethodStandardMethodUsingABlock:^() {
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:"notif" object:nil];
});
}];
I have a method with a completion block, and in this block a notification has to be posted. I don't quite understand why the dispatch_async on the main queue is necessary in this case. The block will already be run on the main thread, and even if it wasn't I don't think it would really matter would it? I would simply have written this:
someMethodStandardMethodUsingABlock:^() {
[[NSNotificationCenter defaultCenter] postNotificationName:"notif" object:nil];
}];
And it does work in my testing.
If you can help me shed some light on this, I'd really appreciate it!
Matt
These 2 sentences from the NSNotificationCenter Class Reference suggest a couple of possible reasons:
A notification center delivers notifications to observers
synchronously. In other words, the postNotification: methods do not
return until all observers have received and processed the
notification.
...
In a multithreaded application, notifications are always delivered in
the thread in which the notification was posted, which may not be the
same thread in which an observer registered itself.
So perhaps (a) the author doesn't want the code to block until all observers have processed the notification, and/or (b) he wants to ensure that the observer methods run on the main thread.
Sometimes you need to run methods that fire some execution asynchronously and return right away. E.g. some of the AppDelegate 'key' methods like applicationDidBecomeActive, or applicationDidEnterBackground, need to be executed and return quickly so the OS doesn't kill your app.
I don't know if that is the case of your question, but it is a possible explanation of the usage of dispatch_async.

CoreData: update in background and reading in main thread causes dead lock

I'm displaying a table with some data to the user. As soon as the view is presented, I'm making a web call to see if there's updated data (asynchronously). When the service call returns, I'd like to update my core data and the view.
Unfortunately I'm often getting dead locks because the view reads the same data as the service call writes. How can I solve this?
When I pause the simulator as soon as it's frozen, the waiting threads are waiting at the following places:
Background (updating) thread: (psynch_cvwait)
[mainContext performSelectorOnMainThread:#selector(mergeChangesFromContextDidSaveNotification:)
withObject:notification
waitUntilDone:YES];
Main thread: (psynch_mutexwait)
performing a filteredArrayUsingPredicate
Thanks a lot!
-mergeChangesFromContextDidSaveNotification: will block the main thread. That call will lock both NSManagedObjectContext instances while it updates the main context with the changes being passed in.
This is generally considered unavoidable in pre-iOS 5 applications. You can minimize it by making more frequent, smaller, saves but it is still going to happen.
The only other option for pre-iOS 5 applications is to tell the main context to -reset: but that will require re-fetching everything -- another delay.
It looks like the main thread is trying to grab some low level lock that the background thread already has (or vice versa). Are you using #synchronized somewhere to provide mutex?
Anyway, is there any reason why your background thread needs to wait for -mergeChangesFromContextDidSaveNotification: to complete? If not, pass NO as the last parameter.
I had the same problem (lock on psynch_cvwait) when I was merging context changes (both ways) between the main and a background context (both using NSConfinementConcurrencyType). The problem was caused by subscribing to NSManagedObjectContextDidSaveNotification on a different queue from which it was sent:
[[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:mainContext
queue:bgQueue
usingBlock:^(NSNotification * _Nonnull note) {
// bgContext runs on bgQueue
[bgContext mergeChangesFromContextDidSaveNotification:note];
}]
As a result the block was never called, and both main and background queues hung on psynch_cvwait()
I fixed it by not blocking mainContext's queue:
[[NSNotificationCenter defaultCenter]
addObserverForName:NSManagedObjectContextDidSaveNotification
object:mainContext
queue:nil
usingBlock:^(NSNotification * _Nonnull note) {
[bgQueue addOperationWithBlock:^{
[bgContext mergeChangesFromContextDidSaveNotification:note];
}];
}]
However, it doesn't seem to be a problem if I block the background queue when merging changes into the main context.

Only call function if another function is ready

How can i make sure in Objective-C, that a function only get called, until another function is ready?
Update:
-(void)reloadJsonFromServer {
[[Sync sharedObject] synchronise];
[self reload];
}
I've got this function.
The second function "reload" should only be called, if the first function is – in this case it's a singlton – is ready. Ready means, that the first function is no more longer running.
So you want to wait on the completion of an asynchronous method? There's a whole bunch of ways to do that.
Make the synchronise method itself call reload on your object when it finishes
dispatch_async the reload method and have it just wait until the other method populates some flag or data structure that you are waiting on before continuing (BOOL synchronised or similar). Note that if your reload method does anything with UIKit, though, then you need to run it on the main thread.
Change the way synchronise runs so it doesn't actually return to the caller until it's done synchronising, but then dispatch_async the reloadJsonFromServer method.
Change synchronise as in the third point, but instead of using dispatch_async, add both of the method calls to an NSOperationQueue as NSOperations, with reload dependent on the completion of synchronise. The operation queue will handle it after that.
Those are just a few, I'm sure other people can suggest more.
In the last few days, i've learnd something about notifications.
I think that is a good way too, to handle something like this. For more information about this look at this blog entry.
Custom Events in Objective-C
A bit late, but with NSNotification it can be handled.
NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
[center addObserverForName:nil
object:nil
queue:nil
usingBlock:^(NSNotification *notification)
{
NSLog(#"%#", notification.name);
}];
Look at this: http://nshipster.com/nsnotification-and-nsnotificationcenter/