NSArrayController и КВО - PullRequest
       47

NSArrayController и КВО

5 голосов
/ 15 января 2010

Что мне нужно сделать, чтобы обновить tableView, связанный с NSArrayController, когда вызывается метод, который обновляет базовый массив? Пример может прояснить это.

Когда мое приложение запускается, оно создает SubwayTrain. Когда SubwayTrain инициализируется, он создает одну SubwayCar. SubwayCar имеет изменяемый массив «пассажиров». Когда машина Метро инициализируется, создается массив пассажиров и добавляется пара объектов People (скажем, человек с именем «сборщик билетов» и еще один с именем «бездомный»). Эти ребята всегда на SubwayCar, поэтому я создаю их при инициализации и добавляю их в массив пассажиров.

За время существования приложения люди садятся в машину. 'addPassenger' вызывается на SubwayCar с человеком, переданным в качестве аргумента.

У меня есть NSArrayController, связанный с subwayTrain.subwayCar.passengers, и при запуске мой сборщик билетов и бездомный парень в порядке. Но когда я использую [subwayCar addPassenger:], tableView не обновляется. Я подтвердил, что пассажир определенно добавлен в массив, но в графическом интерфейсе ничего не обновляется.

Что я могу делать не так? Мой инстинкт заключается в том, что это связано с KVO - контроллер массива не знает, что нужно обновлять при вызове addPassenger (даже если addPassenger вызывает [пассажиров addObject:]. Что я могу ошибаться - я могу опубликовать код, если это поможет. *

Спасибо всем, кто хочет помочь.

UPDATE

Итак, получается, что я могу заставить это работать, изменив метод addPassenger с

[seatedPlayers addObject:person];

до

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];

[newSeatedPlayers addObject:sp];

[seatedPlayers release];

[self setSeatedPlayers:newSeatedPlayers];

Полагаю, это потому, что я использую [self setSeatedPlayers]. Это правильный способ сделать это? Кажется ужасно громоздким копировать массив, освобождать старый и обновлять копию (в отличие от простого добавления в существующий массив).

Ответы [ 5 ]

7 голосов
/ 15 января 2010

Я не знаю, считается ли это ошибкой, но addObject: (и removeObject: atIndex :) не генерируют уведомления KVO, поэтому представление контроллера / таблицы массива не обновляется. Чтобы быть KVO-совместимым, используйте mutableArrayValueForKey:

Пример:

[[self mutableArrayValueForKey:@"seatedPlayers"] addObject:person];

Вы также захотите реализовать insertObject: inSeatedPlayersAtIndex: так как методы KVO по умолчанию очень медленные (они создают целый новый массив, добавляют объект в этот массив и устанавливают исходный массив в новый массив - очень неэффективно) )

- (void)insertObject:(id)object inSeatedPlayerAtIndex:(int)index
{
   [seatedPlayers insertObject:object atIndex:index];
}

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

1 голос
/ 24 декабря 2012

Ключ к магии Наблюдение значения ключа находится в Соответствие значения ключа . Изначально вы использовали имя метода addObject: которое связано только с «неупорядоченным шаблоном доступа», а ваше свойство было индексированным свойством (NSMutableArray). Когда вы изменили свое свойство на неупорядоченное свойство (NSMutableSet), это сработало. Считайте NSArray или NSMutableArray индексированными свойствами, а NSSet или NSMutableSet - неупорядоченными свойствами. Вы действительно должны внимательно прочитать этот раздел, чтобы узнать, что требуется для того, чтобы волшебство произошло ... Соответствие ключевого значения . Есть несколько «Обязательных» методов для разных категорий, даже если вы не планируете их использовать.

1 голос
/ 17 января 2010

Итак, получается, что я могу заставить это работать, изменив метод addPassenger с

[seatedPlayers addObject:person];

до

NSMutableSet *newSeatedPlayers = [NSMutableSet setWithSet:seatedPlayers];
[newSeatedPlayers addObject:sp];
[seatedPlayers release];
[self setSeatedPlayers:newSeatedPlayers];

Полагаю, это потому, что я использую [self setSeatedPlayers]. Это правильный способ сделать это?

Во-первых, это setSeatedPlayers:, с двоеточием. Это жизненно важно в Objective-C.

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

Что вам нужно сделать, это реализовать средства доступа к наборам, такие как addSeatedPlayersObject:. Затем отправьте себе это сообщение. Это делает добавление людей коротким однострочным:

[self addSeatedPlayersObject:person];

И пока вы следуете форматам, совместимым с KVC , вы будете получать уведомления KVO бесплатно, так же, как вы делаете с setSeatedPlayers:.

Преимущества этого по сравнению с setSeatedPlayers::

  • Ваш код для изменения набора будет короче.
  • Поскольку он короче, он будет чище.
  • Использование специальных средств доступа с установленными мутациями предоставляет возможность специальных уведомлений KVO с измененными множествами вместо общих уведомлений об изменениях целого набора.

Я также предпочитаю это решение, а не mutableSetValueForKey:, как для краткости, так и потому, что очень легко неправильно ввести ключ в этом строковом литерале. ( Ули Кустерер имеет макрос для предупреждения, когда это происходит , что полезно, когда вам действительно нужно поговорить с KVC или самим KVO.)

1 голос
/ 15 января 2010

Я не пробовал это, поэтому не могу сказать, что это работает, но разве вы не получите уведомления KVO, вызвав

insertObject: atArrangedObjectIndex:

onArrayController?

0 голосов
/ 14 февраля 2012
  1. Используйте willChangeValueForKey: и didChangeValueForKey:, обернутые вокруг смены участника, когда изменение не вызывает уведомления KVO. Это удобно, когда вы напрямую изменяете переменную экземпляра.

  2. Используйте willChangeValueForKey:withSetMutation:usingObjects: и didChangeValueForKey:withSetMutation:usingObjects:, обернутые вокруг изменения содержимого коллекции, когда изменение не вызывает уведомления KVO.

  3. Используйте [seatedPlayers setByAddingObject:sp], чтобы сделать вещи короче и избежать ненужного выделения изменяемого набора.

В целом, я бы сделал так:

[self willChangeValueForKey:@"seatedPlayers"
            withSetMutation:NSKeyValueUnionSetMutation 
               usingObjects:sp];
[seatedPlayers addObject:sp];
[self didChangeValueForKey:@"seatedPlayers" 
           withSetMutation:NSKeyValueUnionSetMutation 
              usingObjects:sp];

или это:

[self setSeatedPlayers:[seatedPlayers setByAddingObject:sp]];

с последним вариантом, вызывающим автоматический вызов функций, перечисленных в разделе 1. Первый вариант должен быть более эффективным.

...