10

我正在尝试子类化NSNotification.

Apple 的文档NSNotification声明如下:

NSNotification是一个没有实例变量的类簇。因此,您必须继承NSNotification并覆盖原始方法 nameobjectuserInfo。您可以选择任何您喜欢的指定初始化程序,但请确保您的初始化程序不会调用 (via ) NSNotification的实现。不打算直接实例化,并且它的 方法会引发异常。init[super init]NSNotificationinit

但这对我来说不是很清楚。我应该创建这样的初始化程序吗?

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

4 回答 4

14

子类化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

您还可以使用这种方法来简化NSNotification创建。例如,您的类别还可能包括:

+ (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);
}

...
于 2011-09-28T16:28:10.390 回答
2

看来这确实有效。例如:

#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

还要注意与 NSNotifications 相关的大量问题。使用NSNotification notificationWithName:object:生成的 NSNotification 类型是 NSConcreteNotification,而不是 NSNotification。更尴尬的是,如果您正在检查课程,则 NSConcreteNotification 是私有的,因此您没有什么可比较的。

于 2011-09-27T16:27:40.193 回答
1

你没有设置它,确切地说——你只是覆盖了name方法的实现,所以它返回你想要的。换句话说:

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

您的初始化程序看起来不错 - 我之前没有看到不调用其超类实现的示例init,但如果这是文档所说的您应该做的,那么它可能值得一试。

于 2011-09-27T16:00:12.327 回答
1

您可以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 协议扩展的混合,我们也真的在笑。

于 2015-07-15T11:05:51.173 回答