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

发布于 2024-12-06 12:29:15 字数 575 浏览 6 评论 0原文

我正在尝试对 NSNotification 进行子类化。

Apple 的 NSNotification 文档声明如下:

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

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

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

I am trying to subclass NSNotification.

Apple's docs for NSNotificationstate the following:

NSNotification is a class cluster with no instance variables. As such,
you must subclass NSNotification and override the primitive methods
name, object, and userInfo. You can choose any designated initializer
you like, but be sure that your initializer does not call
NSNotification’s implementation of init (via [super init]).
NSNotification is not meant to be instantiated directly, and its init
method raises an exception.

But this isn't clear to me. Should I create an initializer like this?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

酒几许 2024-12-13 12:29:16

子类化 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);
}

...

Subclassing NSNotification is an atypical operation. I think I've only seen it done once or twice in the past few years.

If you're looking to pass things along with the notification, that's what the userInfo property is for. If you don't like accessing things through the userInfo directly, you could use a category to simplify access:

@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

You can also use this approach to simplify NSNotification creation. For example, your category could also include:

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

If, for some strange reason, you'd need the properties to be mutable, then you'd need to use associative references to accomplish that:

#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);
}

...
苦笑流年记忆 2024-12-13 12:29:16

看来这确实有效。例如:

#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 是私有的,所以你没有什么可以比较的。

It seems this does work. For example:

#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

also beware a massive Gotcha related to NSNotifications. The type of NSNotifications greated using NSNotification notificationWithName:object: is NSConcreteNotification, not NSNotification. And to make it a little more awkward, if you are checking for class, NSConcreteNotification is private so you have nothing to compare to.

╰つ倒转 2024-12-13 12:29:16

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

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

你的初始化器看起来不错——我之前没有见过一个不调用其超类实现的 init 示例,但如果这就是文档所说的你应该做的,那么它可能值得尝试一下。

You don’t set it, exactly—you just override the implementation of the name method so it returns what you want. In other words:

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

Your initializer looks fine—I haven’t seen an example of an init that doesn’t call its superclass’s implementation before, but if that’s what the doc’s saying you should do, it’s probably worth a try.

诗酒趁年少 2024-12-13 12:29:16

您可以在发送通知时传递 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 协议扩展,我们也真的很开心。

You can pass a userInfo argument when delivering a notification. Why not create a payload and send that.

// 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);
}

Done.

As a side note: I've found over the years that making a conscious effort to avoid subclassing has made my code more clean, maintainable, changeable, testable and extensible. If you can solve the problem using protocols or categories then you wont lock yourself into the first shoddy design you come up with. With Swift 2.0 protocol extensions in the mix we're really laughing too.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文