Is subclassing NSNotification the right route if I want to add typed properties?

北城以北 提交于 2019-12-10 01:54:59

问题


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

回答1:


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

...



回答2:


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.




回答3:


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.




回答4:


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.



来源:https://stackoverflow.com/questions/7572059/is-subclassing-nsnotification-the-right-route-if-i-want-to-add-typed-properties

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!