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