Objective c 如果我想添加类型化属性,子类化NSNotification是正确的途径吗?

Objective c 如果我想添加类型化属性,子类化NSNotification是正确的途径吗?,objective-c,cocoa,inheritance,nsnotification,class-cluster,Objective C,Cocoa,Inheritance,Nsnotification,Class Cluster,我正在尝试将NSNotification子类化 苹果的NSNotification文档声明如下: NSNotification是一个没有实例变量的类群集。像这样的 您必须将NSNotification子类化并重写原语方法 名称、对象和用户信息。您可以选择任何指定的初始值设定项 您喜欢,但请确保您的初始值设定项不会调用 NSNotification通过[super init]实现init。 NSNotification并不意味着直接实例化,它的init 方法引发异常 但我不清楚。我应该创建这样的初

我正在尝试将NSNotification子类化

苹果的NSNotification文档声明如下:

NSNotification是一个没有实例变量的类群集。像这样的 您必须将NSNotification子类化并重写原语方法 名称、对象和用户信息。您可以选择任何指定的初始值设定项 您喜欢,但请确保您的初始值设定项不会调用 NSNotification通过[super init]实现init。 NSNotification并不意味着直接实例化,它的init 方法引发异常

但我不清楚。我应该创建这样的初始值设定项吗

-(id)initWithObject:(id)object
{
    return self;
}

您不需要设置它,确切地说,您只需要重写name方法的实现,以便它返回您想要的内容。换言之:

- (NSString *)name
{
    return @"Something";
}

您的初始值设定项看起来很好—我以前没有见过不调用其超类实现的init示例,但是如果文档中说您应该这样做,那么可能值得一试。

这似乎确实有效。例如:

#import "TestNotification.h"

NSString *const TEST_NOTIFICATION_NAME = @"TestNotification";

@implementation TestNotification

-(id)initWithObject:(id)object
{
    object_ = object;
    return self;
}

-(NSString *)name
{
    return TEST_NOTIFICATION_NAME;
}

-(id)object
{
    return object_;
}

- (NSDictionary *)userInfo
{
    return nil;
}

@end
还要注意与NSN通知相关的大量Gotcha。使用NSNotification notificationWithName:object:的NSNotification变大的NSNotifications的类型是NSConcreteNotification,而不是NSNotification。更尴尬的是,如果您正在检查类,NSConcreteNotification是私有的,因此您没有什么可比较的。

将NSNotification子类化是一种非典型操作。我想我在过去几年里只见过一两次

如果您希望随通知一起传递信息,那么userInfo属性就是用于此目的的。如果您不喜欢通过userInfo直接访问内容,可以使用类别简化访问:

@interface NSNotification (EasyAccess)

@property (nonatomic, readonly) NSString *foo;
@property (nonatomic, readonly) NSNumber *bar;

@end

@implementation NSNotification (EasyAccess)

- (NSString *)foo {
  return [[self userInfo] objectForKey:@"foo"];
}

- (NSNumber *)bar {
  return [[self userInfo] objectForKey:@"bar"];
}

@end
您还可以使用此方法简化通知创建。例如,您的类别还可以包括:

+ (id)myNotificationWithFoo:(NSString *)foo bar:(NSString *)bar object:(id)object {
  NSDictionary *d = [NSDictionary dictionaryWithObjectsForKeys:foo, @"foo", bar, @"bar", nil];
  return [self notificationWithName:@"MyNotification" object:object userInfo:d];
}
如果出于某种奇怪的原因,您需要属性是可变的,那么您需要使用来实现这一点:

#import <objc/runtime.h>
static const char FooKey;
static const char BarKey;

...

- (NSString *)foo {
  return (NSString *)objc_getAssociatedObject(self, &FooKey);
}

- (void)setFoo:(NSString *)foo {
  objc_setAssociatedObject(self, &FooKey, foo, OBJC_ASSOCIATION_RETAIN);
}

- (NSNumber *)bar {
  return (NSNumber *)objc_getAssociatedObject(self, &BarKey);
}

- (void)setBar:(NSNumber *)bar {
  objc_setAssociatedObject(self, &BarKey, bar, OBJC_ASSOCIATION_RETAIN);
}

...
您可以在传递通知时传递userInfo参数。为什么不创建一个有效负载并发送它呢

// New file:

@interface NotificationPayload : NSObject
@property (copy, nonatomic) NSString *thing;
@end

@implementation NotificationPayload
@end

// Somewhere posting:

NotificationPayload *obj = [NotificationPayload new];
obj.thing = @"LOL";

[[NSNotificationCenter defaultCenter] postNotificationName:@"Hi" object:whatever userInfo:@{ @"payload": obj }];

// In some observer:

- (void)somethingHappened:(NSNotification *)notification
{
  NotificationPayload *obj = notification.userInfo[@"payload"];
  NSLog(@"%@", obj.thing);
}
完成了


附带说明:多年来,我发现有意识地避免子类化使我的代码更加干净、可维护、可更改、可测试和可扩展。如果你能使用协议或类别解决问题,那么你就不会把自己锁定在你提出的第一个劣质设计中。有了Swift 2.0协议扩展,我们也真的笑了。

是的。我意识到我应该在发布这篇文章后大约一秒钟重写并返回所需的值,但在我将其编辑掉之前,您一定已经在那里了。感谢回复。创建NSNotification的子类是非常不寻常的。您能解释一下为什么需要这样做吗?我想使用具有强类型属性的NSNotifications,而不是使用通用的userInfo对象。我还喜欢这样一个事实,子类通知更好地封装了它的用途。我更喜欢[UserPrefsChangedNotification alloc]init]而不是将名称字符串传递给NSNotificationCenter。您可以使用NSNotification类别添加一些属性,然后让方法实现使用关联对象将内容附加到NSNotification。这个类别甚至可以为你提供一个方便的构造器。我的大脑仍然围绕着目标C,我的本能是对它进行子类化,但这确实很有意义。谢谢你的帮助。@DaveDeLong我错过什么了吗?似乎不可能在类别中添加属性。很好的解释。感谢大家的进一步思考,这并不能真正解决我的问题。如果我希望NSNotification的所有实例都获得这一额外功能,那么添加类别是可以的,但我希望该选项具有不同的专门通知。也许其中一个将携带NSArray类型的有效载荷,而另一个可能携带某个类作为其有效载荷。我知道我可以使用userInfo作为通知所携带数据的一种通用转储,但我认为有一个专门的通知要干净得多。这是解决这个问题的一个很好的方法。你可以为每一个你想要的特别通知做一个不同的分类;在大多数情况下,这是一个很好的解决方案。但是,我不理解对分类通知的隐含厌恶。我并不认为这是一种不好的做法——苹果甚至讨论过这样做——而且应该比在性能关键代码中从userInfo字典中查找所有“字段”产生更好的性能。出于这个原因,我在当前项目中使用了NSNotification子类。Dave DeLong,你有没有其他不采取这种方法的具体原因?对于类集群,你应该期望得到任何记录在案的东西,有时任何东西都是对isKindOf做出肯定回答的东西