подклассификация NSCell — [собственное объектное значение] с помощью drawInteriorWithFrame:inView: возвращает странные результаты

#objective-c #cocoa #xcode4 #nscell

#objective-c #cocoa #xcode4 #nscell

Вопрос:

У меня есть NSTableView в моем главном окне файл NIB, который использует динамически созданный источник данных — то есть массив словарей создается динамически при необходимости — в одном из моих классов. Таким образом, он не связан никакими контроллерами в IB.

В tableview есть 2 столбца, которые имеют подкласс DataCell set.

 NSTableColumn *detailsColumn = [[tableView tableColumns] objectAtIndex:0];
imageColumnCell *fileDetails = [[[imageColumnCell alloc] init] autorelease];
[detailsColumn setDataCell:fileDetails];
[fileDetails setNumberOfUploads:numberOfFiles];

NSTableColumn *cancelColumn = [[tableView tableColumns] objectAtIndex:1];
cancelButtonCell *cancelCell = [[[cancelButtonCell alloc] init] autorelease];
[cancelColumn setDataCell:cancelCell];
  

В <NSTableViewDataSource> файле numberOfRowsInTableView: задано правильное количество динамически создаваемого массива, и GDB правильно регистрирует его при построении таблицы.

Теперь возникает проблема. Из гипотетических соображений предположим, что количество массивов для источника данных равно 1

В обоих подклассах NSCell я перезаписал drawInteriorWithFrame:inView: метод для обработки всего рисунка (изображений, текста, кнопок и т.д.) Внутри ячеек таблицы. NSDictionary Для каждого столбца содержит строку (столбец 1) и ссылку на объект для (столбец 2).

Основываясь на примере 1 объекта в источнике данных, если бы в drawInteriorWithFrame:inView: я должен был написать следующее (в столбце 2):

 NSLog(@"drawInteriorWithFrame: in cancelButtonCell - %@", [self objectValue]);
  

Ожидаемый результат (без щелчка по таблице, прокрутки и т.д.) В GDB должен содержать 1 экземпляр этого:

 [0000:000] drawInteriorWithFrame: in cancelButtonCell - <object: 0x123456>
  

И если бы я щелкнул или прокрутил таблицу, то она должна была бы возвращать одинаковые значения для каждого действия в tableview. Однако при первой загрузке я получаю что-то вроде этого:

 [0000:000] drawInteriorWithFrame: in cancelButtonCell - <object: 0x123af06>
[0000:000] drawInteriorWithFrame: in cancelButtonCell - <object: 0x12b3236>
  

Если есть 2 результата, то я получаю 3 элемента журнала, а в других случаях 4. В большинстве случаев метод вызывается на 1 раз больше, чем следовало бы.

Вторая проблема, которая возникла совсем недавно, заключается в том, что если вы обратите внимание на идентификатор объекта выше, вы увидите, что они разные. Если я добавлю 1 элемент к источнику данных, когда я вызываю [self objectValue] , если таблица перерисовывается, идентификатор объекта должен быть таким же, поскольку это ссылка — ie. объект уже -alloc создан в предыдущем классе, и я просто отправляю ссылку на объект.

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

http://forum.soft32.com/mac/NSPopUpButtonCell-view-refresh-ftopict46690.html

Заранее спасибо.

 - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {

  NSString *columnIdentifier = [tableColumn identifier]; 
  NSDictionary *theDict = [fileSourceArray objectAtIndex:row];

  MyAppMethods *appMethods = [MyAppMethods alloc];
  if([appMethods getIsComplete:self])
    return nil;

  if([columnIdentifier isEqualToString:@"fileCancel"]){
    if([[theDict objectForKey:columnIdentifier] isKindOfClass:[ASIFormDataRequest class]]){
      ASIFormDataRequest *fileUploadRequest = [theDict objectForKey:columnIdentifier];
      NSLog(@"in tableView:objectValueForTableColumn:row %@", fileUploadRequest);
      return fileUploadRequest;
    }
  }
  return [theDict objectForKey:columnIdentifier];
}
  

Дополнительный вывод для обсуждения ниже.

Вывод из GDB

** 1 файл, 1 набор NSCell и подкласс ** Здесь запрос остается неизменным — (т. Е. без копирования)

 [6619:903] <ASIFormDataRequest: 0x10ca9b0>
[6619:903] - (id)tableView:objectValueForTableColumn:row: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6619:903] imageColumnCell.m in - (void)drawInteriorWithFrame:inView: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6619:903] - (id)tableView:objectValueForTableColumn:row: <ASIFormDataRequest: 0x10ca9b0>
[6619:903] - (id)tableView:objectValueForTableColumn:row: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6619:903] imageColumnCell.m in - (void)drawInteriorWithFrame:inView: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6619:903] - (id)tableView:objectValueForTableColumn:row: <ASIFormDataRequest: 0x10ca9b0>
  

1 файл, 2 набора NSCell и подкласса

Здесь запрос изменяется в подклассе — (т. Е. без копирования)

 [6652:903] <ASIFormDataRequest: 0x103b390>
[6652:903] - (id)tableView:objectValueForTableColumn:row: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6652:903] imageColumnCell.m in - (void)drawInteriorWithFrame:inView: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6652:903] - (id)tableView:objectValueForTableColumn:row: <ASIFormDataRequest: 0x103b390>
[6652:903] cancelButtonCell.m in - (void)drawInteriorWithFrame:inView: <ASIFormDataRequest: 0x1221b00>
[6652:903] - (id)tableView:objectValueForTableColumn:row: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6652:903] imageColumnCell.m in - (void)drawInteriorWithFrame:inView: /Users/byronrode/Desktop/Monika and Lubo/DSC_3379.jpg
[6652:903] - (id)tableView:objectValueForTableColumn:row: <ASIFormDataRequest: 0x103b390>
[6652:903] cancelButtonCell.m in - (void)drawInteriorWithFrame:inView: <ASIFormDataRequest: 0x1227100>
  

Комментарии:

1. Таблица отображается неправильно, или вы просто обеспокоены очевидным дополнительным вызовом -drawInteriorWithFrame: ? Кроме того, вы смотрели на представленный объект, прежде чем передать его в ячейку? Как это соотносится с тем, что ячейка говорит о своем значении объекта?

2. @josh-caswell — таблица отображается правильно (никаких дополнительных данных). В моем классе, который создает источник данных, фактический объект, словарь и массив, содержащий словарь, содержат один и тот же идентификатор объекта. В источнике данных, в tableView:objectValueForTableColumn:row: на объект по-прежнему правильно ссылаются, но в моем подклассе NSCell ссылка изменяется, и все дополнительные вызовы отличаются от исходной ссылки. Я также заметил, что tableView:objectValueForTableColumn:row: вызывается 3 раза для таблицы из 2 столбцов с одной «строкой» в источнике данных.

3. При дальнейшем тестировании я обнаружил, что «дополнительные вызовы» связаны с количеством столбцов, что стало более очевидным после того, как я не смотрел на это так долго, так что эта часть имеет смысл. Проблема, похоже, заключается в том, что если я удалю второй подкласс DataCell cancelColumn (выше), тогда объект, на который ссылается ссылка, будет правильно идентифицирован. Я обновил вопрос с помощью своего - tableView:objectValueForTableColumn:row: метода.

4. Оба столбца вашей таблицы используют этот же пользовательский класс cell?

5. Нет. Каждый использует отдельный класс cell. Раньше это был один столбец с использованием одной пользовательской ячейки, но была проблема с производительностью при отслеживании мыши, поэтому я разделил его, чтобы отслеживание было связано со столбцом, который нуждался в отслеживании кликов.

Ответ №1:

Я не думаю, что есть о чем беспокоиться, если он отображается правильно. Табличное представление запрашивает в своих столбцах таблицы наличие «фиктивных» ячеек перед завершением их отрисовки; я подозреваю, что это причина дополнительного -draw... вызова. Кроме того, ячейка копирует свое значение объекта при настройке, так что это, очевидно, придаст ей новый адрес.

В ответ на вашу публикацию сообщений журнала:

Мне не на 100% ясно, когда именно публикуются эти журналы и что они отображают, но это похоже на то, что я ожидал увидеть. В tableView:objectValueForTableColumn:row: вы печатаете значение, которое у вас есть в вашем словаре, которое совпадает с исходным ASIFormDataRequest . В drawInteriorWithFrame:inView: вы печатаете ячейку objectValue , которая является копией этого оригинала (то есть нового объекта с другим адресом в памяти).

Комментарии:

1. Это имеет смысл, но, несомненно, копирование тогда было бы таким же при использовании одной или двух пользовательских ячеек столбца. За исключением этого случая, адрес изменяется при использовании 2 пользовательских ячеек. Я добавил больше информации к вопросу. Возможно, теперь это будет иметь больше смысла.

2. Мне кажется, что это детали реализации AppKit, которые не влияют на вашу программу. Я все еще говорю «Не беспокойтесь об этом». Для эффективного выполнения своей работы табличный вид, столбцы таблицы и NSCell все это делает за кулисами то, чего вы, возможно, не ожидаете.