Необходимо ли переопределить bind: toObject: withKeyPath: options: в подклассе NSView для реализации привязки?

У меня есть подкласс NSView, у которого есть свойство, которое я хочу привязать. В подклассе я реализовал следующее:

myView.h:

@property (readwrite, retain) NSArray *representedObjects;

myView.m:

@synthesize representedObjects;

+(void)initialize
{
    [self exposeBinding: @"representedObjects"];
}


-(void)bind:(NSString *)binding toObject:(id)observableController withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
{
    if ([binding isEqualToString:@"representedObjects"]) {
        [observableController addObserver: self forKeyPath:@"arrangedObjects" options:NSKeyValueChangeNewKey context:nil];
    } else {
        [super bind: binding toObject:observableController withKeyPath:keyPath options: options];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if ([keyPath isEqualToString:@"arrangedObjects"]) {
        [self setRepresentedObjects: [object arrangedObjects]];
    }
}

Затем я создаю привязку к arrayController в -[AppController awakeFromNib]:

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

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

Я думал, что NSObject автоматически реализует то, что я сделал вручную в -bind:toObject:withKeyPath:options:, но, похоже, это не так. Если я закомментирую свой -bind:toObject:withKeyPath:options:, метод setRepresentObjects никогда не будет вызван.

Дополнительная информация: я провел дополнительное расследование и пришел к выводу, что мой первоначальный подход верен и вам придется отказаться от -bind:toObject:withKeyPath:options:. Вот цитата из Темы программирования привязок какао: как работают привязки?:

В методе bind: toObject: withKeyPath: options: объект должен как минимум выполнять следующие действия:

  • Определите, какая привязка устанавливается
  • Запишите, к какому объекту он привязан, используя какой путь и с какими параметрами.
  • Зарегистрируйтесь в качестве наблюдателя ключевого пути объекта, к которому он привязан, чтобы получать уведомления об изменениях.

Пример кода в листинге 2 показывает частичную реализацию метода bind: toObject: withKeyPath: options: джойстика, имеющего дело только с привязкой угла.

Листинг 2 Частичная реализация метода bind: toObject: withKeyPath: options для класса Joystick:

static void *AngleBindingContext = (void *)@"JoystickAngle";
 
- (void)bind:(NSString *)binding
 toObject:(id)observableObject
 withKeyPath:(NSString *)keyPath
 options:(NSDictionary *)options
{
 // Observe the observableObject for changes -- note, pass binding identifier
 // as the context, so you get that back in observeValueForKeyPath:...
 // This way you can easily determine what needs to be updated.
 
if ([binding isEqualToString:@"angle"])
 {
    [observableObject addObserver:self
                   forKeyPath:keyPath
                  options:0
                  context:AngleBindingContext];
 
    // Register what object and what keypath are
    // associated with this binding
    observedObjectForAngle = [observableObject retain];
    observedKeyPathForAngle = [keyPath copy];
 
    // Record the value transformer, if there is one
    angleValueTransformer = nil;
    NSString *vtName = [options objectForKey:@"NSValueTransformerName"];
    if (vtName != nil)
    {
        angleValueTransformer = [NSValueTransformer
            valueTransformerForName:vtName];
    }
 }
 // Implementation continues...

Это ясно показывает, что класс Joystick (который является подклассом NSView) должен переопределить -bind:toObject:withKeyPath:options:.

Я нахожу это удивительным. Я скептически отнесся к этому выводу, так как не нашел других примеров кода, которые бы это сделали. Однако, поскольку официальная документация Apple говорит, что я должен отказаться -bind:toObject:withKeyPath:options:, я пришел к выводу, что это правильный подход.

Я был бы очень рад, если бы кто-нибудь смог доказать, что я неправ!


person Community    schedule 14.12.2008    source источник
comment
Связанный вопрос с большей активностью в привязках с двусторонними обновлениями: stackoverflow.com/questions/1169097/   -  person paulmelnikow    schedule 13.09.2011


Ответы (3)


Нет, этот клеевой код не нужен.

Что вы имеете в виду под «похоже, не так»? Что будет, если вы его пропустите?

person Jens Ayton    schedule 14.12.2008

Нет, отменять bind: не нужно.

Как Питер Хози написал в комментарии к предыдущему ответу, вы можете вызвать exposeBinding: и реализовать KVC- и KVO-совместимые аксессоры и сеттеры.

MyView.h:

@interface MyView : NSView {
    NSArray *_representedObjects;
}

// IBOutlet is not required for bindings, but by adding it you can ALSO use
// an outlet
@property (readonly, retain) IBOutlet NSArray *representedObjects;

@end

MyView.m:

+ (void)initialize {
    [self exposeBinding:@"representedObjects"];
}

// Use a custom setter, because presumably, the view needs to re-draw
- (void)setRepresentedObjects:(NSArray *)representedObjects {
    [self willChangeValueForKey:@"representedObjects"];
    // Based on automatic garbage collection
    _representedObjects = representedObjects;
    [self didChangeValueForKey:@"representedObjects"];

    [self setNeedsDisplayInRect:[self visibleRect]];
}

Затем вы можете программно установить привязку:

[myView bind:@"representedObjects" toObject:arrayController withKeyPath:@"arrangedObjects" options: nil];

Однако для установки привязки в Интерфейсном Разработчике необходимо создать настраиваемую палитру.

person paulmelnikow    schedule 12.09.2011

Вам определенно НЕОБХОДИМО реализовать -bind:toObject:withKeyPath:options: в настраиваемом представлении, если вы хотите реализовать привязки в этом представлении. Ваша реализация в myView.m очень удобна.

person Rob Keniger    schedule 08.01.2009
comment
Нет, не знаешь. Вам нужно только вызвать exposeBinding: и реализовать KVC- и KVO-совместимые аксессоры для каждого связываемого свойства. @property и @synthesize сделают последнее за вас. - person Peter Hosey; 08.01.2009
comment
Интересно, ты прав. Я всегда делал это долго, поэтому я собираюсь удалить код. - person Rob Keniger; 08.01.2009