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

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

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

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

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

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

4 ответа

Решение

Наследование 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);
}

...

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

#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

также остерегайтесь массивного Gotcha, связанного с NSNotifications. Тип NSNotifications, настроенный с использованием NSNotificationtificationWithName:object: это NSConcreteNotification, а не NSNotification. И чтобы сделать его немного более неловким, если вы проверяете класс, NSConcreteNotification является частным, поэтому вам не с чем сравнивать.

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

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

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

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

Другие вопросы по тегам