【问题标题】:Test type of NSNotificationNSNotification 的测试类型
【发布时间】:2011-09-28 12:46:16
【问题描述】:

我需要检查一个对象是否是 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

【问题讨论】:

  • 您测试的对象是 NSNotification no 的子类?
  • 你可以使用objective-C的isMemberOfClass方法。请参考[isMemberOfClass][1][1]:stackoverflow.com/questions/2045561/…
  • 对象可以是 1. NSConcreteNotification 或 2. NSNotification 的子类。我需要区分。
  • 我已更新问题以澄清。

标签: objective-c ios nsnotification


【解决方案1】:

没有理由按照您描述的方式对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 打印通知要容易得多。更容易序列化它们。更容易复制它们。等等。相关的参考资料很棒,但它们确实增加了很多小问题,如果你能侥幸逃脱的话,应该避免这些情况。

【讨论】:

  • 非常好。这真的很有帮助。非常感谢。
【解决方案2】:

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

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

【讨论】:

  • 我完全理解这是一个坏主意,这就是我试图寻找替代方案的原因。我想使用具有自定义属性的通知,这意味着我别无选择,只能将 NSNotification 子类化,Apple 在其文档中说这很好。我的部分要求是,当我收到通知时,我可以检查它是 NSNotification 还是 NSNotification 的子类,以便我可以采取适当的行动。
  • 您可以在不使用关联对象的类别的情况下添加属性...
  • 我的印象是不可能在一个类别中综合属性?
  • 是的,但您可以使用关联对象。看看这个:developer.apple.com/library/mac/#documentation/cocoa/conceptual/…看看我更新的答案......
  • 谢谢。我去看看。
【解决方案3】:

要测试 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);
}

【讨论】:

  • 感谢您的回复。但是使用字符串似乎是一种非常不安全的方式。对于这样一个简单的任务,我们不得不求助于对字符串进行硬编码,这似乎很奇怪。
  • 为什么要区分这两者?
  • 我正在将通知映射到命令,并且我希望该选项是强类型的。在我看来,依赖字符串(通知的名称)是一种糟糕的方式。
  • 但是询问实现细节也是如此...NS*Notification 对我们来说应该就像NSNotification...
【解决方案4】:

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

[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];

【讨论】:

  • 更惯用的(实现更短,使用时间更长)可能是if ([notification respondsToSelector:@selector(performMyFeature)]) [notification performMyFeature];,然后你就不再需要hasMyFeature 属性了。
  • @Quuxplusone 是的。现在想想,你也可以让category添加一个空的performMyFeature方法,那么你根本不需要检查。但我会保持原样,因为在很多情况下我更喜欢这种风格。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多