Наблюдение за значениемValueForKeyPath: ofObject: change: context: с массивами работает неправильно - PullRequest
0 голосов
/ 14 февраля 2009

У меня есть объект, который реализует индексированные методы доступа для ключа с именем contents. В этих методах доступа я вызываю willChange:valuesAtIndexes:forKey: и didChange:valuesAtIndexes:forKey:, когда изменяю базовый массив.

У меня также есть пользовательский объект просмотра, связанный с contents через NSArrayController. В observeValueForKeyPath:ofObject:change:context: единственное значение в словаре изменений для NSKeyValueChangeKindKey, которое я когда-либо видел, это NSKeyValueChangeSetting. Когда я добавляю объекты в массив, я ожидаю увидеть NSKeyValueChangeInsertion.

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

Ответы [ 3 ]

1 голос
/ 15 февраля 2009

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

Я не понимаю, что вы подразумеваете под реализацией методов доступа к массивам в представлении.

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

Привязки строятся поверх КВО.

И КВК.

Все привязки реализованы с использованием observeValueForKeyPath:

Переопределение это один из способов, конечно. Другой способ - реализовать средства доступа в объекте со свойством bindable (представление).

Мое пользовательское представление обеспечивает привязку, которую приложение привязывает к массиву - или в данном случае к контроллеру массива. Методы доступа применяются к KVC, а не к KVO.

Привязки какао вызовут для вас аксессоры вашего представления (предположительно с использованием KVC). Вам не нужно реализовывать метод наблюдения KVO (если, конечно, вы не используете KVO напрямую).

Я знаю это, потому что я сделал это таким образом. См. PRHGradientView в Загрузка ЦП .

Любопытно, что в документации это не упоминается. Я собираюсь подать документацию об ошибке - либо я делаю что-то хрупкое, либо они забыли упомянуть эту очень хорошую функцию в документации.

Совершенно важно, что я получаю сообщение об установке при каждом обновлении массива. Я бы не стал публиковать это как вопрос, если бы это не имело значения.

Есть довольно большое количество людей, которые занимаются тем, что называется «преждевременная оптимизация». У меня нет возможности узнать, кто один из них, не спросив.

0 голосов
/ 15 февраля 2009

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

Я не понимаю, что вы подразумеваете под реализацией методов доступа к массивам в представлении. Привязки строятся поверх КВО. Все привязки реализуются с использованием observeValueForKeyPath: Мое настраиваемое представление обеспечивает привязку, которую приложение привязывает к массиву - или, в данном случае, к контроллеру массива. Методы доступа применяются к KVC, а не к KVO.

Абсолютно важно, что я получаю сообщение об установке при каждом обновлении массива. Я бы не стал публиковать это как вопрос, если бы это не имело значения. Я называю что-то вроде

[[modelObject mutableArrayValueForKey:@"contents"] addObjectsFromArray:hundredsOfObjects];

При каждой вставке мое представление просматривает целый новый массив. Поскольку я потенциально добавляю сотни объектов, это O(N^2), когда оно должно быть O(N). Это совершенно неприемлемо, кроме проблем с производительностью. Но, поскольку вы упомянули об этом, представление должно выполнить значительный объем работы, чтобы наблюдать целый новый массив, что значительно замедляет работу программы.

Я не могу отказаться от использования контроллера массива, потому что мне нужна фильтрация и сортировка, а также потому, что NSTableView привязан к тому же контроллеру. Я полагаюсь на это, чтобы синхронизировать сортировку и выборку.

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

0 голосов
/ 14 февраля 2009

У меня есть объект, который реализует индексированные методы доступа для ключа, называемого содержимым. В этих методах доступа я вызываю willChange: valuesAtIndexes: forKey: и didChange: valuesAtIndexes: forKey: когда я изменяю базовый массив.

Не делай этого. KVO публикует уведомления для вас, когда вы получаете сообщение одному из этих методов доступа.

У меня также есть пользовательский объект представления, связанный с содержимым через NSArrayController. В НаблюденииValueForKeyPath: ofObject: change: context: единственное значение в словаре изменений для NSKeyValueChangeKindKey, которое я когда-либо вижу, является NSKeyValueChangeSetting. Когда я добавляю объекты в массив, я ожидаю увидеть NSKeyValueChangeInsertion.

Во-первых, почему вы используете KVO напрямую? Используйте bind:toObject:withKeyPath:options:, чтобы привязать свойство представления к свойству arrangedObjects контроллера массива (я полагаю) и реализовать средства доступа к массиву (включая индексированные средства доступа, если хотите) в представлении.

Для другого помните, что arrangedObjects является производным свойством. Контроллер массива будет фильтровать и сортировать свой массив содержимого; результат arrangedObjects. Можно утверждать, что перестановка индексов из исходной вставки в новую вставку была бы более точным переводом первого изменения во второе, но установка всего массива arrangedObjects, вероятно, была проще реализовать (что-то вроде [self _setArrangedObjects:[[newArray filteredArrayUsingPredicate:self.filterPredicate] sortedArrayUsingDescriptors:self.sortDescriptors]]).

Это действительно имеет значение? Вы профилировали и обнаружили, что ваше приложение работает медленно с оптовой заменой массива?

Если это так, вам может потребоваться привязать представление непосредственно к свойству content массива или к исходному массиву базового объекта и потерять свободную фильтрацию и сортировку.

...