Наблюдение за NSMutableArray для вставки/удаления
Класс имеет свойство (и экземпляр var) типа NSMutableArray с синтезированными аксессуарами (через @property
). Если вы наблюдаете этот массив, используя:
[myObj addObserver:self forKeyPath:@"theArray" options:0 context:NULL];
И затем вставьте объект в массив следующим образом:
[myObj.theArray addObject:NSString.string];
Уведомление observValueForKeyPath... отправлено не. Однако следующее сообщение отправляет надлежащее уведомление:
[[myObj mutableArrayValueForKey:@"theArray"] addObject:NSString.string];
Это потому, что mutableArrayValueForKey
возвращает прокси-объект, который заботится об уведомлении наблюдателей.
Но не должны ли синтезированные аксессоры автоматически возвращать такой прокси-объект? Каким образом можно обойти это - написать ли пользовательский аксессуар, который просто вызывает [super mutableArrayValueForKey...]
?
Ответы
Ответ 1
Но не должны ли синтезированные аксессоры автоматически возвращать такой прокси-объект?
Нет.
Каким образом можно обойти это - написать ли пользовательский аксессуар, который просто вызывает [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:
являются последними, которые я проверил, только признанными KVO для свойств set (как в NSSet), а не свойствами массива, поэтому вы не получаете от них бесплатных уведомлений KVO, если только вы не реализовать их поверх аксессуаров, которые он распознает. Я подал ошибку об этом: x-radar://problem/6407437
У меня список всех форматов селектора доступа в моем блоге.
Ответ 2
Я не использовал бы willChangeValueForKey
и didChangeValueForKey
в этой ситуации. Во-первых, они предназначены для указания того, что значение на этом пути изменилось, а не изменения значений во многих отношениях. Вместо этого вы бы хотели использовать willChange:valuesAtIndexes:forKey:
, если бы сделали это так. Тем не менее, использование ручных уведомлений KVO, подобных этому, - плохая инкапсуляция. Лучший способ сделать это - определить метод addSomeObject:
в классе, который фактически владеет массивом, который будет включать в себя уведомления KVO вручную. Таким образом, внешним методам, добавляющим объекты в массив, не нужно беспокоиться о том, как обращаться с владельцем массива KVO, что не будет очень интуитивным и может привести к ненужному коду и, возможно, к ошибкам, если вы начнете добавлять объекты к массив из нескольких мест.
В этом примере я бы продолжал использовать mutableArrayValueForKey:
. Я не уверен в изменяемых массивах, но я считаю, что, прочитав документацию, этот метод фактически заменяет весь массив новым объектом, поэтому, если производительность вызывает беспокойство, вы также захотите реализовать insertObject:in<Key>AtIndex:
и removeObjectFrom<Key>AtIndex:
в класс, которому принадлежит массив.
Ответ 3
когда вы просто хотите наблюдать за изменением счетчика, вы можете использовать совокупный путь ключа:
[myObj addObserver:self forKeyPath:@"[email protected]" options:0 context:NULL];
но имейте в виду, что любое переупорядочение в массиве не срабатывает.
Ответ 4
Собственный ответ на свой вопрос почти прав. Не отгружайте theArray
извне. Вместо этого объявите другое свойство theMutableArray
, не соответствующее переменной экземпляра, и напишите этот аксессор:
- (NSMutableArray*) theMutableArray {
return [self mutableArrayValueForKey:@"theArray"];
}
В результате другие объекты могут использовать thisObject.theMutableArray
для внесения изменений в массив, и эти изменения активируют KVO.
Другие ответы, указывающие на то, что эффективность увеличивается, если вы также реализуете insertObject:inTheArrayAtIndex:
и removeObjectFromTheArrayAtIndex:
, все еще верны. Но нет необходимости в том, чтобы другие объекты должны были знать об этом или называть их напрямую.
Ответ 5
Если вам не нужен сеттер, вы также можете использовать более простую форму ниже, которая имеет аналогичную производительность (тот же темп роста в моих тестах ) и менее шаблонной.
// 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>
. Если он найдет один, прокси отправит все сообщения этому объекту.
См. эти документы Apple, раздел "Шаблон поиска доступа для упорядоченных коллекций", №3.
Ответ 6
Вам нужно обернуть вызов addObject:
в вызовы willChangeValueForKey:
и didChangeValueForKey:
. Насколько я знаю, для NSMutableArray вы не можете узнать о каких-либо наблюдателях, наблюдающих за своим владельцем.
Ответ 7
одним из решений является использование 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;
}
чем вы получаете KVO и можете сравнивать старый и новый массивы
ПРИМЕЧАНИЕ: self.myArray не должен быть nil, иначе arrayByAddingObject: результат также равен nil
В зависимости от случая это может быть решением, и поскольку NSArray хранит только указатели, это не слишком важно, если вы не работаете с большими массивами и частыми операциями