Наблюдение за NSMutableArray для вставки / удаления


класс имеет свойство (и экземпляр var) типа NSMutableArray с синтезированными аксессорами (через @property). Если вы наблюдаете этот массив с помощью:

[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];

а затем вставьте объект в массив следующим образом:

[myObj.theArray addObject:NSString.string];

observeValueForKeyPath... уведомление является не отправлено. Тем не менее, следующее отправляет соответствующее уведомление:

[[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string];

это так mutableArrayValueForKey возвращает прокси-объект, который заботится об наблюдатели.

но разве синтезированные методы доступа не должны автоматически возвращать такой прокси-объект? Что это правильный способ обойти это ... я должен написать пользовательский метод, который просто вызывает [super mutableArrayValueForKey...]?

7 73

7 ответов:

но разве синтезированные методы доступа не должны автоматически возвращать такой прокси-объект?

нет.

что это правильный способ обойти это-я должен написать пользовательский метод, который просто вызывает [super mutableArrayValueForKey...]?

нет. Реализовать методов доступа к массиву. При их вызове KVO автоматически отправит соответствующие уведомления. Так что все, что вам нужно сделать, это:

[myObject insertObject:newObject inTheArrayAtIndex:[myObject countOfTheArray]];

и Правильная вещь произойдет автоматически.

для удобства вы можете написать addTheArrayObject: - аксессоры. Этот метод доступа вызовет один из реальных методов доступа к массиву, описанных выше:

- (void) addTheArrayObject:(NSObject *) newObject {
    [self insertObject:newObject inTheArrayAtIndex:[self countOfTheArray]];
}

(вы можете и должны заполнить соответствующий класс для объектов в массиве, вместо NSObject.)

затем, вместо [myObject insertObject:…] вы пишите [myObject addTheArrayObject:newObject].

к сожалению, add<Key>Object: партнеры remove<Key>Object: являются, последний раз я проверял, только распознается кво для набора (как в nsset) свойства, а не свойства массива, поэтому вы не получаете бесплатные уведомления KVO с ними, если вы не реализуете их поверх методов доступа, которые он распознает. Я подал ошибку об этом: x-radar: / / problem / 6407437

у меня есть список всех форматов селектора доступа на моем блоге.

Я бы не использовать willChangeValueForKey и didChangeValueForKey в этой ситуации. Во-первых, они должны указывать на то, что значение на этом пути изменилось, а не на то, что значения в отношении ко многим меняются. Вы хотели бы использовать willChange:valuesAtIndexes:forKey: вместо этого, если вы сделали это таким образом. Тем не менее, использование ручных уведомлений KVO, подобных этому, является плохой инкапсуляцией. Лучший способ сделать это-определить метод addSomeObject: в классе, который фактически владеет массивом, который будет включать в себя ручные уведомления KVO. Таким образом, вне методы, которые добавляют объекты в массив, также не должны беспокоиться об обработке KVO владельца массива, что было бы не очень интуитивно понятным и могло бы привести к ненужному коду и, возможно, ошибкам, если вы начнете добавлять объекты в массив из нескольких мест.

в этом примере я бы на самом деле продолжал использовать mutableArrayValueForKey:. Я не уверен с изменяемыми массивами, но я считаю, что из чтения документации этот метод фактически заменяет весь массив новым объектом, поэтому если производительность-это проблема, которую вы также захотите реализовать insertObject:in<Key>AtIndex: и removeObjectFrom<Key>AtIndex: в классе, который владеет массива.

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

[myObj addObserver:self forKeyPath:@"theArray.@count" options:0 context:NULL];

но имейте в виду, что любое переупорядочение в theArray не будет срабатывать.

ваш собственный ответ на ваш собственный вопрос почти прав. Не венд theArray внешне. Вместо того, чтобы объявить другую собственность, theMutableArray, не соответствует переменной экземпляра, и напишите этот метод доступа:

- (NSMutableArray*) theMutableArray {
    return [self mutableArrayValueForKey:@"theArray"];
}

в результате другие объекты могут использовать thisObject.theMutableArray внести изменения в массив, и эти изменения вызвать кво.

другие ответы, указывающие на то, что эффективность повышается, если вы также реализуете insertObject:inTheArrayAtIndex: и removeObjectFromTheArrayAtIndex: по-прежнему верны. Но там нет необходимости, чтобы другие объекты знали об этом или вызывали их напрямую.

Если вам не нужен сеттер, вы также можете использовать более простую форму ниже ,которая имеет аналогичную производительность (то же самое рост в моих тестах) и менее шаблонный.

// Interface
@property (nonatomic, strong, readonly) NSMutableArray *items;

// Implementation
@synthesize items = _items;

- (NSMutableArray *)items
{
    return [self mutableArrayValueForKey:@"items"];
}

// Somewhere else
[myObject.items insertObject:@"test"]; // Will result in KVO notifications for key "items"

это работает, потому что если методы доступа к массиву не реализованы и нет задатчика для ключа, mutableArrayValueForKey: будет искать переменную экземпляра с именем _<key> или <key>. Если он находит один, прокси будет пересылать все сообщения на этот объект.

посмотреть эти Яблока документы, раздел "шаблон поиска доступа для упорядоченных коллекций", #3.

вам нужно обернуть ваш addObject: вызов в willChangeValueForKey: и didChangeValueForKey: звонки. Насколько я знаю, нет никакого способа для NSMutableArray, который вы модифицируете, чтобы узнать о каких-либо наблюдателях, наблюдающих за его владельцем.

одним из решений является использование NSArray и создание его с нуля путем вставки и удаления, например

- (void)addSomeObject:(id)object {
    self.myArray = [self.myArray arrayByAddingObject:object];
}

- (void)removeSomeObject:(id)object {
    NSMutableArray * ma = [self.myArray mutableCopy];
    [ma removeObject:object];
    self.myArray = ma;
}

чем вы получаете кво и можете сравнить старый и новый массив

Примечание: self.myArray не должен быть nil, иначе arrayByAddingObject: results in nil too

в зависимости от случая, это может быть решением, и поскольку NSArray хранит только указатели, это не так много накладных расходов, если вы не работаете с большими массивами и частыми операциями