Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

ArtOfWarfare

macrumors G3
Original poster
Nov 26, 2007
9,672
6,212
I use NSUserNotification when my app is run on an OS that has them and NSAlerts when it's run on an OS that lacks them. My code looks like this:

Code:
- (void)deployWarningWithTitle:(NSString*)title andMessage:(NSString*)message {
    if (NSClassFromString(@"NSUserNotification")) {
        NSUserNotification* notification = [[NSUserNotification alloc] init];
        notification.soundName = NSUserNotificationDefaultSoundName;
        notification.title = title;
        notification.informativeText = message;
        [[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification:notification];
        [notification release];
    } else {
        NSAlert* alert = [[NSAlert alloc] init];
	alert.messageText = title;
	alert.informativeText = message;
	[alert runModal];
	[alert release];
    }
}

My Deployment Target is 10.5 while my Base SDK is 10.9.

Yet when my application is run on OS X 10.7.5 (and possibly other, older platforms) this error appears before any other part of my code is called:

Code:
dyld: Symbol not found: _OBJC_CLASS_$_NSUserNotification
Referenced from: /Users/taylor/Dropbox/Battery Status Stuff/Battery Status Builds/1.4.1b/Battery Status.app/Contents/MacOS/Battery Status
Expected in: /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation

The only linked libraries I have are:

SystemConfiguration.framework
ServiceManagement.framework
libz.dylib
IOBluetooth.framework
IOKit.framework
Cocoa.framework

What am I doing wrong? How can I fix this error without losing either compatibility with OS X 10.5 - 10.9 but still display NSUserNotifications in versions of OS X that allow it?
 
Last edited:
In this code:
Code:
    if (NSClassFromString(@"NSUserNotification")) {
        NSUserNotification* notification = [[NSUserNotification alloc] init];
you have a compile-time reference to the class NSUserNotification. What happens if that class doesn't exist at run-time, such as when the code is run on 10.5? When dyld tries to resolve that symbol, it will fail. And what is the symbol for the class? It's _OBJC_CLASS_$_NSUserNotification.

While you may not be executing that branch on a 10.5 run-time, the symbol for the class is still present, and dyld will still try to resolve it. This is a side-effect of dyld loading the program: it tries to resolve all dependent libraries and symbols. Read the entire document on the Obj-C runtime, and how symbols and method-names are resolved. Also look at the Dynamic Loading docs.

Also look at the sample code linked at the doc for NSClassFromString():
https://developer.apple.com/library...Foundation_Functions/Reference/reference.html


The way to fix this is to use the Class object returned by NSClassFromString. I'm pretty sure there's Apple documentation on how to do this (try looking in the Dynamic Loading docs), but it's pretty straightforward. A class's class methods are a Class object's instance-methods. So where NSUserNotification has a +alloc method, the Class object returned by NSClassFromString has a -alloc method. These are the exact same method.

Also, you may have to eliminate the type declaration of notification as a NSUserNotification reference. Instead, declare it as 'id' type. You won't get compile-time checks that the methods are correct for NSUserNotification, but the run-time message-send checks will still be there.
 
Thanks for the help chown :D

The changes I made:
1 - Moved NSClassFromString(@"NSUserNotification") outside of the if so that I could assign it to a variable of type Class.
2 - Used the Class variable instead of a compile time symbol to access NSUserNotification. Stored the alloc/inited instance as an id instead of an NSUserNotification*.
3 - Used the method notation instead of property notation for references to NSUserNotification (I don't understand why Xcode always has warnings about properties that might not exist but it's okay with methods that might not exist.)
4 - I replaced the NSUserNotificationCenter symbol with NSClassFromString(@"NSUserNotificationCenter").

I've checked and this code runs on every version of OS X that I want to support:

Code:
- (void)deployWarningWithTitle:(NSString*)title andMessage:(NSString*)message {
    [b]Class nsUserNotificationClass = NSClassFromString(@"NSUserNotification");[/b]
    if ([b]nsUserNotificationClass[/b]) {
        [b]id[/b] notification = [[[b]nsUserNotificationClass[/b] alloc] init];
        [notification [b]set[/b]SoundName:NSUserNotificationDefaultSoundName];
        [notification [b]set[/b]Title:title];
        [notification [b]set[/b]InformativeText:message];
        [[[b]NSClassFromString(@"NSUserNotificationCenter")[/b] defaultUserNotificationCenter] deliverNotification:notification];
        [notification release];
    } else {
        NSAlert* alert = [[NSAlert alloc] init];
	alert.messageText = title;
	alert.informativeText = message;
	[alert runModal];
	[alert release];
    }
}
 
Last edited:
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.