Спостереження за NSMutableArray для вставки / видалення


76

Клас має властивість (і екземпляр var) типу NSMutableArray із синтезованими доступними засобами (через @property). Якщо ви спостерігаєте цей масив, використовуючи:

А потім вставте об’єкт у масив таким чином:

Повідомлення obserValueForKeyPath ... не надсилається. Однак наступне надсилає належне повідомлення:

Це тому, що mutableArrayValueForKeyповертає проксі-об'єкт, який дбає про сповіщення спостерігачів.

Але чи не повинні синтезовані засоби доступу автоматично повертати такий проксі-об'єкт? Який правильний спосіб обійти це - чи варто писати власний доступ, який просто викликає [super mutableArrayValueForKey...]?

Відповіді:


79

Але чи не повинні синтезовані засоби доступу автоматично повертати такий проксі-об'єкт?

Ні.

Який правильний спосіб обійти це - чи варто писати власний доступ, який просто викликає [super mutableArrayValueForKey...]?

Ні. Реалізуйте засоби доступу до масиву . Коли ви зателефонуєте їм, KVO автоматично опублікує відповідні повідомлення. Отже, все, що вам потрібно зробити, це:

і Правильна річ відбудеться автоматично.

Для зручності ви можете написати addTheArrayObject:аксесуар. Цей аксесуар викликав би один із справжніх массивів, описаних вище:

(Ви можете і повинні заповнити відповідний клас для об'єктів у масиві замість NSObject .)

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

Як не сумно add<Key>Object:і його аналогremove<Key>Object: , востаннє я перевірив, розпізнається KVO лише за властивостями набору (як у NSSet), а не властивостями масиву, тому ви не отримуєте з ними безкоштовних сповіщень KVO, якщо ви не застосуєте їх поверх доступних для нього розпізнавань . Я повідомив про помилку з цього приводу: x-radar: // problem / 6407437

У моєму блозі є список усіх форматів селектора доступу .


5
Проблема №1 полягає в тому, що коли ви додаєте спостерігача, ви спостерігаєте властивість якогось об’єкта. Масив - це вартість цієї властивості, а не сама властивість. Ось чому вам потрібно або використовувати засоби доступу, або -mutableArrayValueForKey: для зміни масиву.
Кріс Хенсон,

Ваш останній пункт, здається, застарів - я отримую безкоштовні сповіщення KVO про властивості NSArray, за умови, що я реалізую як додавання, так і видалення доступу.
Брайан

9

Я б не використовував willChangeValueForKeyі didChangeValueForKeyв цій ситуації. По-перше, вони мають на меті вказати, що значення на цьому шляху змінилося, а не те, що значення у відносинах до багатьох змінюються. Ви хотіли б використовувати willChange:valuesAtIndexes:forKey:замість цього, якби зробили це таким чином. Незважаючи на це, використання таких ручних сповіщень KVO є поганою інкапсуляцією. Кращим способом це є визначення методу addSomeObject:в класі, який фактично володіє масивом, який включав би ручні сповіщення KVO. Таким чином, зовнішнім методам, які додають об'єкти до масиву, не потрібно турбуватися про обробку KVO власника масиву, що не буде дуже інтуїтивно зрозумілим і може призвести до непотрібного коду та можливо помилок, якщо ви почнете додавати об'єкти до масив з кількох місць.

У цьому прикладі я б продовжував використовувати mutableArrayValueForKey:. Я не впевнений у змінних масивах, але, перечитавши документацію, я вважаю, що цей метод фактично замінює весь масив новим об’єктом, тому, якщо продуктивність викликає занепокоєння, ви також захочете реалізувати це insertObject:in<Key>AtIndex:і removeObjectFrom<Key>AtIndex:в класі, який володіє масивом .


7

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

але майте на увазі, що будь-яке переупорядкування в масиві не спрацює.


Або заміни щодо цього, наприклад, коли використовується [-replaceObjectAtIndex: withObject:].
Patrick Pijnappel,

3

Ваша власна відповідь на власне запитання майже правильна. Не продавайте theArrayзовні. Натомість оголосіть іншу властивість, що не theMutableArrayвідповідає жодній змінній екземпляру, і напишіть цей пристрій доступу :

Результатом є те, що інші об’єкти можуть використовувати thisObject.theMutableArrayдля внесення змін до масиву, і ці зміни викликають KVO.

Інші відповіді вказують, що ефективність зростає, якщо ви також впроваджуєте insertObject:inTheArrayAtIndex:і removeObjectFromTheArrayAtIndex:все ще правильні. Але немає необхідності в тому, щоб інші об’єкти мали про них знати або називати їх безпосередньо.


Використовуючи цей ярлик, я можу спостерігати зміни лише за ключовим шляхом "theArray", а не "theMutableArray".
Michael Mior

Окрім цього, все функціонує чудово, і я можу маніпулювати theMutableArrayтак, ніби це NSMutableArrayвластивість.
Майкл Міор

Коли я намагаюся ліниво ініціювати цей проксі-об'єкт, повідомлення KVO не видається. Ти знаєш чому? - (NSMutableArray *) theMutableArray {if (_theMutableArray) повертає _theMutableArray; _theMutableArray = [self mutableArrayValueForKey: @ "theArray"]; return _theMutableArray; }
Luong Huy Duc

2

Якщо вам не потрібен сеттер, ви також можете скористатися простішою формою нижче, яка має подібні показники (однаковий темп зростання в моїх тестах) і менший шаблон.

Це працює, тому що якщо доступ до масиву не реалізовано, і немає ключа для налаштування, mutableArrayValueForKey:буде шукати змінну екземпляра з ім'ям _<key>або <key>. Якщо його знайде, проксі-сервер буде пересилати всі повідомлення цьому об’єкту.

Див. Ці документи Apple , розділ "Шаблон пошуку доступу для впорядкованих колекцій", №3.


Чомусь це у мене не працює. У цьому випадку я міг би бути справді сліпим.
Ентальпі,

1
Використовуючи це рішення, обов’язково позначайте свої властивості як readonly, інакше це застрягне у нескінченній петлі ...
Symaxion

0

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


0

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

ніж ви отримуєте KVO і можете порівняти старий і новий масив

ПРИМІТКА: self.myArray не повинен бути рівним нулю, інакше arrayByAddingObject: також призводить до нуля

Залежно від випадку, це може бути рішенням, і оскільки NSArray зберігає лише вказівники, це не надто накладні витрати, якщо ви не працюєте з великими масивами та частими операціями

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.