Как добавить объект в программно связанный NSMutableArray? - PullRequest
1 голос
/ 17 апреля 2009

У меня есть NSDocument, который имеет следующую структуру:

@interface MyDocument : NSDocument
{
    NSMutableArray *myArray;

    IBOutlet NSArrayController *myArrayController;
    IBOutlet MyView *myView;
}
@end

Я создаю экземпляр NSArrayController и MyView в MyDocument.xib и установил соединения с владельцем файла (MyDocument), поэтому я совершенно уверен, что с точки зрения Interface Builder я все сделал правильно.

Интерфейс для MyView прост:

@interface MyView : NSView {
    NSMutableArray *myViewArray;
}
@end

Теперь, в MyDocument windowControllerDidLoadNib, у меня есть следующий код:

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];
    [myArrayController setContent:myArray];
// (This is another way to do it)    [myArrayController bind:@"contentArray" toObject:self withKeyPath:@"myArray" options:nil];

    [myView bind:@"myViewArray" toObject:myArrayController withKeyPath:@"arrangedObjects" options:nil];
}

В отладчике я проверил, что myViewArray является NSControllerArrayProxy, поэтому может показаться, что моя программная привязка верна. Однако, когда я пытаюсь добавить объекты в методах MyView к MyView myViewArray, они не появляются для обновления myArray MyDocument. Я пробовал оба следующих подхода:

[myViewArray addObject:value];
[self addMyViewArraysObject:value];

(Второй подход вызывает ошибку компилятора, как и ожидалось, но я думал, что среда выполнения Objective C "реализует" этот метод в соответствии с моим ограниченным пониманием KVO.)

Что-то не так с тем, как я пытаюсь обновить myViewArray? Что-то не так с моей программной привязкой? (Я пытаюсь сделать это программно, потому что MyView - это пользовательский вид, и я не хочу создавать для него палитру IB.)

Ответы [ 4 ]

1 голос
/ 10 июля 2009

Во-первых, нет ничего плохого в связывании сранжированными объектами: например, для элемента NSTableColumn его содержимое должно быть привязано только к расположенным объектам, а его значения для содержимого - расположены к расположенному по адресуObjects.someProperty.

Распространенной ошибкой является расстановка организованных объектов в качестве содержимого элемента arrayController, но это, как вы видели, приведет к горестям: layoutObjects - это представление того, как arrayController в настоящее время упорядочивает объекты в своем содержимом, а не содержимое сам по себе.

Тем не менее, способ связать массив с arrayController:

[self.myArrayController bind:NSContentArrayBinding 
 toObject:self
 withKeyPath:@"myView.myViewArray" 
 options:nil]; 

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

Теперь вы можете добавлять объекты, вызывая addObject для arrayController , так как это является обязанностью контроллера.

[self.myArrayController addObject: anObject]
1 голос
/ 17 апреля 2009

Проблема заключается в том, что я связывал MyView myViewArray со свойством NSArrayController arrangedObjects вместо его свойства content.

При связывании с arrangedObjects я обнаружил, что фактический объект, на который указывает myViewArray, является экземпляром NSControllerArrayProxy. Я не нашел однозначного ответа относительно того, что на самом деле делает этот объект, когда я искал в Интернете дополнительную информацию о нем. Однако примеры кода, которые я обнаружил, предполагают, что NSControllerArrayProxy предназначен для предоставления удобства доступа к свойствам объектов в массиве , а не самих объектов (в массиве). Вот почему я считаю, что я ошибся в привязке к arrangedObjects.

Решением было вместо этого связать MyView myViewArray со свойством NSArrayController content:

- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
    [super windowControllerDidLoadNib:aController];

    [myArrayController setContent:myArray];
    [myView bind:@"myViewArray" toObject:myArrayController withKeyPath:@"content" options:nil];
}

Несмотря на то, что это работает, я не уверен на 100%, что в этом случае привязка к content является правильной. Если кто-то может пролить свет на программную привязку к различным свойствам NSArrayController, я хотел бы получить комментарии к этому ответу. Спасибо.

1 голос
/ 17 апреля 2009

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

KVO переопределяет ваши методы доступа (если вы соответствуете определенным форматам ) и публикует необходимые уведомления. Вы не получите этого, когда говорите напрямую с вашим массивом; все, что связано с этим свойством, не будет знать, что вы изменили это свойство, если вы явно не укажете его. Когда вы используете методы доступа, KVO сообщает вам другие объекты.

Единственный раз, когда вы не используете ваши методы доступа (синтезированные или иные), это init и dealloc, поскольку вы будете разговаривать с наполовину init ed или - dealloc объектом ked.

Как только вы используете собственные методы доступа для изменения массива и, таким образом, получаете бесплатные уведомления KVO, все должно работать:

  • Представление при изменении его свойства автоматически уведомляет контроллер массива, который изменяет свое свойство content, которое уведомляет ваш контроллер.
  • Ваш контроллер при изменении его свойства автоматически уведомляет контроллер массива, который изменяет его свойство arrangedObjects, которое уведомляет представление.
1 голос
/ 17 апреля 2009

Я вижу две возможности здесь:

Во-первых, вы создаете экземпляр объекта NSMutableArray (и освобождаете его) в своем классе MyDocument? Это должно выглядеть примерно так:

- (id)init
{
    if ((self = [super init]) == nil) { return nil; }
    myArray = [[NSMutableArray alloc] initWithCapacity:0];
    return self;
}

- (void)dealloc
{
    [myArray release];
    [super dealloc];
}

Во-вторых, вы объявили myViewArray как свойство в MyView? Это должно выглядеть примерно так:

// MyView.h:
@interface MyView : NSView
{
    NSMutableArray * myViewArray;
}
@property (assign) NSMutableArray * myViewArray;
@end
    // MyView.m:
    @implementation MyView
    
    @synthesize myViewArray;
    
    @end
    

    Кроме этого, мне кажется, что вы правильно выполнили все привязки.

    обновление: Как насчет использования NSArrayController для добавления элементов в массив:

    // MyView.h:
    @interface MyView : NSView
    {
        NSMutableArray * myViewArray;
        IBOutlet NSArrayController * arrayController;
    }
    @property (assign) NSMutableArray * myViewArray;
    - (void)someMethod;
    @end
    
      // MyView.m:
      @implementation MyView
      
      @synthesize myViewArray;
      
      - (void)someMethod
      {
          id someObject = [[SomeClass alloc] init];
          [arrayController addObject:[someObject autorelease]];
      }
      
      @end
      
      ...