NSNotification的测试类型

时间:2011-09-28 12:46:17

标签: objective-c ios nsnotification

我需要检查对象是否是NSNotification。仅仅知道它是否是一个子类是不够的,因为我想区分它是NSNotification还是NSNotification的子类。

所以详细说明我需要区分以下内容:

  1. NSConcreteNotification
  2. NSNotification的子类(但不是NSConcreteNotification)
  3. 问题是NSNotifications实际上是NSConcreteNotifications,NSConcreteNotification是私有类,所以我不能用它来测试。

    [object isMemberOfClass: [NSNotification class]] // returns NO in both cases
    [object isKindOfClass: [NSNotification class]] // returns YES in both cases
    

4 个答案:

答案 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];