Нужны советы по поводу моделей Какао MVC / KVO - PullRequest
4 голосов
/ 01 декабря 2010

Это очень широкий / расплывчатый вопрос, но здесь идет. Заранее извиняюсь.

Приложение (приложение для настольного компьютера), которое я создаю, использует различные виды ввода для создания QR-кода (я просто создаю его, чтобы изучить Obj-C / Cocoa). Пользователь может переключаться между различными представлениями, которые позволяют вводить простой текст (одно текстовое поле), данные VCard / MeCard (несколько текстовых полей) и другие вещи. Независимо от ввода, результат - QR-код.

Чтобы сохранить содержимое, я хотел бы использовать представления в качестве контроллеров представления, чтобы они обрабатывали свои собственные входные данные и могли просто "отправлять" общие "данные для кодирования" объекта, содержащего все данные, в Центральный энкодер. То есть текстовое представление создаст объект данных с текстом его текстового поля, в то время как представление VCard / MeCard будет использовать все свои поля для создания структурированных данных VCard / MeCard.

Я могу связать все это вместе вручную в коде, но мне бы очень хотелось узнать, как привязки / KVO могут помочь мне. Увы, после прочтения документации Apple для разработчиков и более простых учебников / примеров, которые я смог найти, я все еще не уверен, как применить его к моему приложению.

Например: пользователь редактирует текстовые поля в VCard-представлении. Контроллер представления VCard уведомляется о каждом обновлении и «пересчитывает» объект данных. Центральный контроллер кодера затем уведомляется об обновленном объекте данных и кодирует данные.

Смысл всего этого в том, что входные представления могут быть созданы совершенно независимо и могут содержать все виды полей ввода. Затем они обрабатывают свои собственные входные данные и «возвращают» общий объект данных, который может использовать кодировщик. Внутри представления просматривают свои входные данные для обновления объекта данных, а внешне кодировщику нужно только наблюдать объект данных.

Беда в том, что я понятия не имею, как заставить все это происходить и поддерживать его в стороне Должен ли быть контроллер объекта между входным представлением и его полями? Должен ли быть другой между представлением и кодером? Что мне нужно где? Если у кого-то есть ссылка на хороший учебник, поделитесь.

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

1 Ответ

2 голосов
/ 31 декабря 2010

Определенно неопределенный вопрос, но от одного новичка к другому я чувствую вашу боль :) 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 :), это сработает. С одним диспетчером на объект, который желает получать любое количество уведомлений об изменениях в любом количестве объектов.

Что мне нравится в этом:

  1. У вас может быть только один observeValueForKeyPath на класс, но вы можете наблюдать несколько вещей. Естественно следующая мысль: «эй, может быть, я могу передать селектор»
  2. Да, но невозможно передать несколько аргументов через performSelector, если не используются объекты-оболочки, такие как NSNotification. Кто хочет убирать предметы обертки.
  3. Переопределение observeValueForKeyPath, когда суперкласс также использует KVO, усложняет любые общие подходы - вы должны знать, какие уведомления передать суперклассу, а какие оставить.
  4. Кто в любом случае хочет повторно реализовать один и тот же общий основанный на селекторе observeValueForKeyPath? Лучше всего сделать это один раз и использовать повторно.

Хорошим вариантом может быть добавление еще одного поля, например id additionalContext, в KVODispatcher и передача этого дополнительного объектаContext в вызове objc_msgSend. Может быть полезно использовать его для хранения объекта пользовательского интерфейса, который должен обновляться при изменении наблюдаемых данных. Даже, возможно, NSArray.

...