1

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

因此,要详细说明,我需要区分以下内容:

  1. 一个 NSConcreteNotification
  2. NSNotification 的子类(但不是 NSConcreteNotification)

问题是 NSNotifications 实际上是 NSConcreteNotifications 而 NSConcreteNotification 是一个私有类,所以我不能用它来测试。

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

4 回答 4

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。更容易序列化它们。更容易复制它们。等等。相关的参考文献很棒,但它们确实增加了很多小案例,如果你能侥幸逃脱的话,你应该避免这些案例。

于 2011-09-28T13:45:40.717 回答
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")])
{ /* ... */ }

如果要向NSNotifications 添加属性,则应查看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);
}
于 2011-09-28T12:54:51.533 回答
1

这听起来真是个坏主意。当您第一次收到通知时,您已经知道它是什么类型,因为它作为显式参数传递给通知回调方法。考虑将通知存储为另一个对象的强类型属性,或者如果要将其添加到集合中,则将其插入到字典中的适当键下,或者将其传递给不保留类型信息的其他方法以使其更容易以后鉴定。

创建对私有 API 的依赖项(包括私有类的名称)将使您的代码更加脆弱,并且更有可能在未来的版本中中断。显然,这些类是私有的原因之一是为了让 Apple 的工程师更容易在他们认为合适的时候更改它们。例如,NSArray 和 NSMutableArray 使用的具体子类在最近的 SDK 版本中发生了变化。

于 2011-09-28T13:08:35.740 回答
1

正如其他人指出的那样,依赖私有类的名称是一个坏主意。如果您正在寻找一个特定的子类,您可以明确地检查该类。

[notification isMemberOfClass:[MyNotificationSubclass class]];

您可以使用多个语句来检查多个子类,但这会有点混乱。每次添加要查找的新类时,此方法也需要更改。定义一个 readonly 属性可能会更好,该属性指示通知是否支持您正在寻找的功能,因此您不必依赖类,而是依赖类的能力。您可以在 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];
于 2011-09-28T14:17:15.693 回答