我需要检查对象是否是NSNotification。仅仅知道它是否是一个子类是不够的,因为我想区分它是NSNotification还是NSNotification的子类。
所以详细说明我需要区分以下内容:
问题是NSNotifications实际上是NSConcreteNotifications,NSConcreteNotification是私有类,所以我不能用它来测试。
[object isMemberOfClass: [NSNotification class]] // returns NO in both cases
[object isKindOfClass: [NSNotification class]] // returns YES in both cases
答案 0 :(得分:2)
没有理由按照你描述的方式继承NSNotification
。首先,NSNotification
已经携带了userInfo字典。您可以将所需的任何数据放在那里。如果您愿意,可以使用类别方法读取和写入该字典(我一直这样做)。例如,我想要做的一件非常普遍的事情是传递一些对象,比如RNMessage
。所以我创建了一个如下所示的类别:
@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];
}
正如@hypercrypt所指出的,你也可以使用相关的引用将数据附加到任意对象而不创建ivar,但使用NSNotification
使用userInfo字典要简单得多。使用NSLog
打印通知要容易得多。更容易序列化它们。更容易复制它们。等等。相关参考文献很棒,但它们确实添加了许多小角落案例,如果你可以侥幸逃脱,你应该避免这些案例。
答案 1 :(得分:1)
测试id对象是NSNotification
使用:
[object isMemberOfClass:[NSNotification class]];`
测试它是否为NSConcreteNotifications
使用
[object isMemberOfClass:NSClassFromString(@"NSConcreteNotifications")];
根据需要将字符串更改为其他类的名称...
然后,您可以将两个检查合并为“NSNotification的子类(但不是NSConcreteNotification”。
或者:
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
}
或者
if ([object isKindOfClass:[NSNotification class]] && ![object isMemberOfClass:NSClassFromString(@"NSConcreteNotifications")])
{ /* ... */ }
如果您要向NSNotification
添加属性,请查看Associative References。
基本理念是:
static const char objectKey;
- (id)object
{
return objc_getAssociatedObject(self, &objectKey);
}
- (void)setObject:(id)object
{
objc_setAssociatedObject(self, &objectKey, object, OBJC_ASSOCIATION_RETAIN);
}
答案 2 :(得分:1)
这听起来真的很糟糕。当您第一次收到通知时,您已经知道它是什么类型,因为它作为通知回调方法的显式参数传递。考虑将通知存储为另一个对象的强类型属性,或者如果要将其添加到集合中,则将其插入到适当键下的字典中,或者将其传递给不保留类型信息的其他方法以使其更容易以后确定。
在私有API上创建依赖关系(包括私有类的名称)将使您的代码更加脆弱,并且更有可能在将来的版本中中断。显然,这些类是私有的原因之一是让Apple的工程师更容易在他们认为合适时更改它们。例如,NSArray和NSMutableArray使用的具体子类刚刚在最新版本的SDK中发生了变化。
答案 3 :(得分:1)
正如其他人所指出的那样,依靠私人类的名称是一个坏主意。如果您正在寻找一个特定的子类,您可以只显式检查该类。
[notification isMemberOfClass:[MyNotificationSubclass class]];
您可以使用多个语句来检查多个子类,但这会有点混乱。每次添加要查找的新类时,此方法还需要更改。最好定义一个只读属性来指示通知是否支持您正在寻找的功能,因此您不会像课程的能力那样依赖于该类。您可以在NSNotification上使用一个类别,该类别只返回此属性的NO
,并且具有该功能的任何子类都将覆盖返回YES
的方法。
@interface NSNotification (MyFeature)
@property (readonly) BOOL hasMyFeature;
@end
@implementation NSNotification (MyFeature)
- (BOOL)hasMyFeature {
return NO;
}
@end
在支持它的子类中:
- (BOOL)hasMyFeature {
return YES;
}
- (void)performMyFeature {
...
}
这还允许您通过更改为hasMyFeature
返回的标记来更改通知是否启用了该功能,并且您的检查代码只是:
if(notification.hasMyFeature) [notification performMyFeature];