Определенно неопределенный вопрос, но от одного новичка к другому я чувствую вашу боль :) 1001 *
Я скачал и распаковал каждый пример и часто просматривал их. Я обнаружил, что это самое ценное, что поможет мне преодолеть горб. Я определенно рекомендую не сдаваться на примерах. Я взломал этот скрипт , чтобы загрузить и распаковать их все.
С точки зрения хороших паттернов КВО, я нашел метод , описанный здесь , очень полезным. Однако не работает как есть в Objective-C 2.0. Также он не дает много подробностей о том, как это на самом деле используется. Вот что у меня работает:
KVODispatcher.h
вот так:
#import <Foundation/Foundation.h>
@interface KVODispatcher : NSObject {
id owner;
}
@property (nonatomic, retain) id owner;
- (id) initWithOwner:(id)owner;
- (void)startObserving:(id)object keyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options
selector:(SEL)sel;
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context;
@end
А KVODispatcher.m
таков:
#import "KVODispatcher.h"
#import <objc/runtime.h>
@implementation KVODispatcher
@synthesize owner;
- (id)initWithOwner:(id)theOwner
{
self = [super init];
if (self != nil) {
self.owner = theOwner;
}
return self;
}
- (void)startObserving:(id)object
keyPath:(NSString*)keyPath
options:(NSKeyValueObservingOptions)options
selector:(SEL)sel
{
// here is the actual KVO registration
[object addObserver:self forKeyPath:keyPath options:options context:sel];
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
// The event is delegated back to the owner
// It is assumed the method identified by the selector takes
// three parameters 'keyPath:object:change:'
objc_msgSend(owner, (SEL)context, keyPath, object, change);
// As noted, a variation of this technique could be
// to expand the data passed in to 'initWithOwner' and
// have that data passed to the selected method here.
}
@end
Тогда вы можете зарегистрироваться, чтобы наблюдать за событиями так:
KVODispatcher* dispatcher = [[KVODispatcher alloc] initWithOwner:self];
[dispatcher startObserving:theObject
keyPath:@"thePath"
options:NSKeyValueChangeNewKey
selector:@selector(doSomething:object:change:)];
И в том же объекте, который выполнил вышеизложенное, у вас может быть такой метод:
- (void) doSomething:(NSString *)keyPath
object:(id)object
change:(NSDictionary *)change {
// do your thing
}
Вы можете использовать столько методов типа doSomething, сколько пожелаете. Пока они используют одни и те же параметры (keyPath: object: change :), это сработает. С одним диспетчером на объект, который желает получать любое количество уведомлений об изменениях в любом количестве объектов.
Что мне нравится в этом:
- У вас может быть только один
observeValueForKeyPath
на класс, но вы можете наблюдать несколько вещей. Естественно следующая мысль: «эй, может быть, я могу передать селектор»
- Да, но невозможно передать несколько аргументов через
performSelector
, если не используются объекты-оболочки, такие как NSNotification
. Кто хочет убирать предметы обертки.
- Переопределение
observeValueForKeyPath
, когда суперкласс также использует KVO, усложняет любые общие подходы - вы должны знать, какие уведомления передать суперклассу, а какие оставить.
- Кто в любом случае хочет повторно реализовать один и тот же общий основанный на селекторе
observeValueForKeyPath
? Лучше всего сделать это один раз и использовать повторно.
Хорошим вариантом может быть добавление еще одного поля, например id additionalContext
, в KVODispatcher и передача этого дополнительного объектаContext в вызове objc_msgSend
. Может быть полезно использовать его для хранения объекта пользовательского интерфейса, который должен обновляться при изменении наблюдаемых данных. Даже, возможно, NSArray.