Является ли подкласс NSNotification правильным маршрутом, если я хочу добавить типизированные свойства? - PullRequest
10 голосов
/ 27 сентября 2011

Я пытаюсь создать подкласс NSNotification.

Документы Apple для NSNotification указывают следующее:

NSNotification - это кластер классов без переменных экземпляра.Таким образом, вы должны создать подкласс NSNotification и переопределить примитивные методы name, object и userInfo.Вы можете выбрать любой назначенный вам инициализатор, но убедитесь, что ваш инициализатор не вызывает NSNotification реализацию init (через [super init]).NSNotification не предназначен для непосредственного создания экземпляра, и его метод init вызывает исключение.

Но это мне не ясно.Должен ли я создать такой инициализатор?

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

Ответы [ 4 ]

14 голосов
/ 28 сентября 2011

Подклассы 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);
}

...
2 голосов
/ 27 сентября 2011

Кажется, это работает.Например:

#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.Тип NSNotifications, выделенных с использованием NSNotificationtificationWithName: object: , является NSConcreteNotification, а не NSNotification.И чтобы сделать его немного более неловким, если вы проверяете класс, NSConcreteNotification является частным, поэтому вам не с чем сравнивать.

1 голос
/ 15 июля 2015

Вы можете передать аргумент 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 в миксе мы тоже смеемся.

1 голос
/ 27 сентября 2011

Вы не устанавливаете это точно, вы просто переопределяете реализацию метода name, чтобы он возвращал то, что вы хотите.Другими словами:

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

Ваш инициализатор выглядит нормально - я не видел пример init, который не вызывает реализацию своего суперкласса раньше, но если это то, что, по словам доктора, вы должны сделать, наверное стоит попробовать.

...