Reputation: 43589
I need to check whether an object is an NSNotification. It is not enough to know if it is a subclass, as I want to differentiate between whether it is an NSNotification or a subclass of NSNotification.
So to elaborate I need to differentiate between the following:
The problem is that NSNotifications are actually NSConcreteNotifications and NSConcreteNotification is a private class so I can't use it to test against.
[object isMemberOfClass: [NSNotification class]] // returns NO in both cases
[object isKindOfClass: [NSNotification class]] // returns YES in both cases
Upvotes: 1
Views: 1372
Reputation: 39905
As others have pointed out, it is a bad idea to rely on the name of a private class. If you are looking for one specific subclass, you could just explicitly check for that class.
[notification isMemberOfClass:[MyNotificationSubclass class]];
You could use multiple statements to check for multiple subclasses, but that would be a little cluttered. This method also requires changes every time you add a new class to look for. It may be better to define a readonly property which indicates whether a notification supports the feature you are looking for, so you aren't relying on the class so much as the ability of the class. You could use a category on NSNotification which simply returns NO
for this property, and any subclasses which have the feature would override the method to return YES
.
@interface NSNotification (MyFeature)
@property (readonly) BOOL hasMyFeature;
@end
@implementation NSNotification (MyFeature)
- (BOOL)hasMyFeature {
return NO;
}
@end
In the subclasses which support it:
- (BOOL)hasMyFeature {
return YES;
}
- (void)performMyFeature {
...
}
This would also allow you to change whether or not a notification has the feature enabled by changing a flag which is returned for hasMyFeature
, and your checking code would simply be:
if(notification.hasMyFeature) [notification performMyFeature];
Upvotes: 1
Reputation: 299275
There is no reason to subclass NSNotification
the way you're describing. First, NSNotification
already carries a userInfo dictionary. You can put any data you want in there. You can use category methods to read and write into that dictionary if you like (I do this all the time). For example, a very common thing I want to do is pass along some object, say the RNMessage
. So I create a category that looks like this:
@interface NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message;
@end
@interface NSNotification (RNMessage)
- (RNMessage *)message;
@end
static NSString * const RNMessageKey = @"message";
@implementation NSNotificationCenter (RNMessage)
- (void)postNotificationName:(NSString *)aName object:(id)anObject message:(RNMessage *)message {
[self postNotificationName:aName object:anObject userInfo:[NSDictionary dictionaryWithObject:message forKey:RNMessageKey];
}
@end
@implementation NSNotification (RNMessage)
- (RNMessage *)message {
return [[self userInfo] objectForKey:RNMessageKey];
}
As @hypercrypt notes, you can also use associated references to attach data to any arbitrary object without creating an ivar, but with NSNotification
it's much simpler to use the userInfo dictionary. It's much easier to print notification using NSLog
. Easier to serialize them. Easier to copy them. Etc. Associated references are great, but they do add lots of little corner cases that you should avoid if you can get away with it.
Upvotes: 2
Reputation: 15376
To test id object is an NSNotification
use:
[object isMemberOfClass:[NSNotification class]];`
To test if it is a NSConcreteNotifications
use
[object isMemberOfClass:NSClassFromString(@"NSConcreteNotifications")];
Change the string to the name of a different class as needed...
You can then combine the two check for 'A Subclass of NSNotification (But not NSConcreteNotification'.
Either:
if ([object isMemberOfClass:NSClassFromString(@"NSConcreteNotifications")])
{
// It's a NSConcreteNotifications...
}
else if ([object isKindOfClass:[NSNotification class]])
{
// It's an NSNotification (or subclass) but not an NSConcreteNotifications
}
Or
if ([object isKindOfClass:[NSNotification class]] && ![object isMemberOfClass:NSClassFromString(@"NSConcreteNotifications")])
{ /* ... */ }
If you want to add properties to NSNotification
s you should look into Associative References.
The basic idea is:
static const char objectKey;
- (id)object
{
return objc_getAssociatedObject(self, &objectKey);
}
- (void)setObject:(id)object
{
objc_setAssociatedObject(self, &objectKey, object, OBJC_ASSOCIATION_RETAIN);
}
Upvotes: 1
Reputation: 15597
That sounds like a really bad idea. When you first receive the notification, you already know what type it is, because it's passed as an explicit argument to a notification callback method. Consider storing the notification as a strongly typed property of another object, or inserting in a dictionary under an appropriate key if you're adding it to a collection, or passing it to other methods that don't preserve the type information to make it easier to identify later.
Creating dependencies on private API (including the names of private classes) will make your code more fragile, and much more likely to break in a future release. Obviously, one of the reasons these classes are private is to make it easier for Apple's engineers to change them as they see fit. For example, the concrete subclasses used by NSArray and NSMutableArray just changed in a recent release of the SDK.
Upvotes: 1