Why can't I observe NSApplicationXYZNotification with addObserverForName? - objective-c

I'm trying to make an Objective-C iOS library work for macOS applications. Removing UIKit-based notifications is the only real task here. When trying to replace—for example—UIApplicationDidEnterBackgroundNotification with NSApplicationDidResignActiveNotification, I run into the odd error of the latter variable being called an undeclared identifier.
[self.observers addObject: [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidResignActiveNotification object:nil queue:nil usingBlock:^(NSNotification* note) {
// stop operations
}]];
The same error does not arise when I use the alternate method:
[[NSNotificationCenter defaultCenter] addObserver:[???] selector:[some selector] name:NSApplicationDidResignActiveNotification object:nil];
The problem with this method though is that I have to have an observer beforehand—like self—as opposed to receiving one that I can add to the self.observers array.
I've read a smattering of docs, questions, and guides—including the NSApplication doc and the NSHipster guide—but I can't seem to figure out my misunderstanding, though I believe it is something fundamental about NSNotificationCenter and how it works.

Silly solution, though aided by #Willeke's comment that they weren't seeing an error with the same code snippet—the library needed Cocoa.h in place of the iOS imports.

Related

GCController framework (OSX) not generating notifications

I'm trying to integrate the GCController framework into my project. I'm not so familiar with Objective-C, so please excuse my ignorance around how this is supposed to work.
I have a class that extends NSOpenGLView, which registers observer methods for the various controller notifications, like so:
-(void)awakeFromNib
{
// Do some stuff
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(controllerStateChanged) name:GCControllerDidConnectNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(controllerStateChanged) name:GCControllerDidDisconnectNotification object:nil];
}
and I've defined a super simple handler as such:
- (void)controllerStateChanged {
NSLog (#"something happened, let's check it out\n");
}
Problem is, these events/notifications never seem to fire - and when I inspect [GCController controllers], it's entirely empty.
Of course, it stands to reason that if there are no controllers, there will be no events - so maybe I'm doing something wrong?
Or perhaps, for whatever reason - my controller simply fails to generate the required events (I'm using a PS4 controller which is registered with the OS, be it wirelessly or via USB, so I'm not sure what I'm missing here).
Is there some other place I need to enable these notifications? Do I need to somehow initialise the GCController framework?
So it turns out that the GCController framework only supports MFi (Made For iPod, Made For iPhone, Made For iPad) controller products.
For anyone looking to support non-MFi controllers, you'll need to interface with the HID via IOKit. Or use the rather excellent DDHIDLib instead

Remove an NSNotification observer that may not exist

I can not seem to find a definitive answer on this topic.
Is it okay to remove an observer that may not exist?
Example Code:
-(void)commonInit{
[[NSNotificationCenter defaultCenter]removeObserver:self];
[[NSNotificationCenter defaultCenter]addObserver:self
selector:#selector(userDidChangePrecision:)
name:kUser_Changed_Precision
object:nil];
}
-(void)dealloc{
[[NSNotificationCenter defaultCenter]removeObserver:self];
[super dealloc];
}
This would prevent more than one observer being initialized for the object in the case where the object may be reinitialized during run time.
Snippet from the Apple docs:
- (void)removeObserver:(id)notificationObserver
Parameters
*notificationObserver*
The observer to remove. Must not be nil.
- (void)removeObserver:(id)notificationObserver name:(NSString *)notificationName object:(id)notificationSender
Parameters
*notificationObserver*
Observer to remove from the dispatch table. Specify an observer to remove only entries for this observer. Must not be nil, or message will have no effect.
In both cases, the warning that observer not be nil is overstated; the effect, in both cases, is that this message has no effect. Neither compiler nor runtime errors, no zombies, &c.
Likewise, specifying an observer that is not observing also has no effect.
Not a definitive answer, but based on observations and investigations of playing with trial-and-error code such as:
[[NSNotificationCenter defaultCenter] removeObserver:nil];
[[NSNotificationCenter defaultCenter] removeObserver:[UIView new]];
I can't find definitive documentation on if it's allowed to remove non-existent observers but I think the NSNotificationCenter documentation can be read in this way. It says that removeObserver:name:object: removes matching observers. I'm just assuming that this includes no matching observers.
But here's another reason why your approach might be harmful: When your commonInit method is being called other code (sub- or superclasses' init) might already have registered to notifications. When subclassing a UIViewController that's even likely (for memory warnings).
So I'd say you should never unconditionally unregister from notification center, except in dealloc.

Memory pointlessly allocated with ARC

I'm observing iTunes using the NSDistributedNotificationCenter class.
This method is called when iTunes plays a new track. The implementation is empty.
Strangely, when I have ARC enabled, there is some memory allocated every time this method observeITunes: method gets called. Apparently, this memory is never released.
No matter how long I wait, this memory is never released.
So I found this strange and made an empty test-project, copying the relevant code there.
In the test-project there is no memory allocation visible at all (Which is correct).
Code
[[NSDistributedNotificationCenter defaultCenter] addObserver:self
selector:#selector(observeITunes:)
name:#"com.apple.iTunes.playerInfo"
object:nil];
- (void)observeITunes:(NSNotification *)notification {
// Empty method
}
Can anyone explain this phenomenon to me?
I couldn't find an answer.

NSMetaDataQuery never calls back with NSMetadataQueryDidFinishGatheringNotification

For an iCloud plugin I'm writing, I subscribe my iCloud manager class to these iCloud NSMetaDataQuery observers:
// Add a predicate for finding the documents
NSString* filePattern = [NSString stringWithFormat:#"*.%#", #"*"];
self.metadataQuery = [[NSMetadataQuery alloc] init];
// Before starting to query, it is required to set the search scope.
arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
// It is also required to set a search predicate.
[self.metadataQuery setPredicate:[NSPredicate predicateWithFormat:#"%K LIKE %#", NSMetadataItemFSNameKey, filePattern]];
// Listen for the second phase live-updating
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryDidReceiveNotification:) name:NSMetadataQueryDidUpdateNotification object:nil];
// Listen for the first phase gathering
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(queryIsDoneGathering:) name:NSMetadataQueryDidFinishGatheringNotification
object:nil];
[self.metadataQuery startQuery];
The problem is that none of these selectors are actually ever called back, not even once, and I especially need the NSMetaDataQueryDidUpdateNotification to track upload/download progress of files in the cloud.
An odd thing is that I had this working the other day, but somehow it just stopped working and I've starred myself blind in trying to figure out what the problem actually is. By subscriping to the NSMetadataQueryDidStartGatheringNotification I can see that it does start, but it's like it never finishes. It is quite weird.
I was wondering if anyone have any clue at all as what to be wrong with the above code? Or where else I can look for the problem.
Thank you for your time :)
Make sure you start the NSMetadataQuery in the main thread. Back then this requirement was not documented.
This was quite the secret to dig up. I don't know if you've given up by now, but at long last, I might be able to help.
It turns out that for some (all?) C++ app configurations, there is a message pump that doesn't run automatically. In my app, I finally started getting my expected callbacks after placing a loop like this following my [m_query startQuery] call:
// make it look syncronous for now
while( m_state != finished )
{
Check_For_Interruption();
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:timeslice]]; //<--**this line is the key**
}
where the callbacks are setup to correctly update the m_state variable.
My example just blocks, using it's own thread timeslice to run the loop (unless interrupted by a timeout being reached), but this could also be set up in an asynchronous way.
Another thing to note for those who go overboard on legacy support:
This method did cause the app to start leaking mach ports on OS X 10.9 and older. Haven't seen any problems on anything newer than that, though.

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.