Como adicionar um objeto para um NSMutableArray programaticamente ligada?
-
09-09-2019 - |
Pergunta
Eu tenho um NSDocument que tem a seguinte estrutura:
@interface MyDocument : NSDocument
{
NSMutableArray *myArray;
IBOutlet NSArrayController *myArrayController;
IBOutlet MyView *myView;
}
@end
Eu instanciar o NSArrayController eo MyView em MyDocument.xib, e fizeram as conexões para o dono do arquivo (MyDocument), então eu tenho certeza que, do ponto de vista da Interface Builder, tenho feito tudo corretamente.
A interface para MyView é simples:
@interface MyView : NSView {
NSMutableArray *myViewArray;
}
@end
Agora, em MyDocument windowControllerDidLoadNib
, eu tenho o código a seguir:
- (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];
}
No depurador, tenho verificado que myViewArray
é um NSControllerArrayProxy, então parece que a minha ligação programática está correto. No entanto, quando eu tento adicionar objetos em métodos de MyView ao myViewArray
MyView, eles não aparecem para atualizar myArray
do MyDocument. Eu tentei ambas as seguintes abordagens:
[myViewArray addObject:value];
[self addMyViewArraysObject:value];
(A segunda abordagem faz com que um erro do compilador, como esperado, mas eu pensei que o tempo de execução Objective-C seria "implementar" este método por minha compreensão limitada de KVO.)
Existe errado algo com a forma como eu estou tentando atualização myViewArray
? Existe errado algo com ligação minha programático? (Eu estou tentando fazer isso programaticamente, porque MyView é uma exibição personalizada e eu não quero criar uma paleta IB para ele.)
Solução
O problema é que você está transformando sua matriz diretamente. Implementar indexada métodos de acesso e chamar aqueles.
KVO substitui seus métodos de acesso (contanto que você se conforma com determinados formatos ) e mensagens as notificações necessárias. Você não consegue isso quando você falar diretamente com sua matriz; qualquer coisa ligada à propriedade não vai saber que você mudou a propriedade, a menos que você diga a ele explicitamente. Quando você usa os seus métodos de acesso, KVO diz aos outros objetos para você.
A única vez para não usar seus métodos de acesso (sintetizado ou não) está em init
e dealloc
, desde que você estaria falando com um objeto de metade init
ed ou -dealloc
ked.
Uma vez que você estiver usando seus próprios métodos de acesso a mutação da matriz, e obtendo, assim, as notificações KVO livres, as coisas devem apenas trabalho:
- A vista, quando mutação sua propriedade, irá notificar automaticamente o controlador de matriz, que transforma sua propriedade
content
, que notifica o controlador. - O seu controlador, quando mutação sua propriedade, irá notificar automaticamente o controlador de matriz, que transforma sua propriedade
arrangedObjects
, que notifica a vista.
Outras dicas
Eu posso ver duas possibilidades aqui:
Primeiro, você instanciar o objeto NSMutableArray
(e liberá-lo) em sua classe MyDocument
? Deve ser algo como isto:
- (id)init
{
if ((self = [super init]) == nil) { return nil; }
myArray = [[NSMutableArray alloc] initWithCapacity:0];
return self;
}
- (void)dealloc
{
[myArray release];
[super dealloc];
}
Em segundo lugar, se você declarar myViewArray como uma propriedade em MyView
? Deve ser algo como isto:
// MyView.h:
@interface MyView : NSView
{
NSMutableArray * myViewArray;
}
@property (assign) NSMutableArray * myViewArray;
@end
// MyView.m:
@implementation MyView
@synthesize myViewArray;
@end
Além disso, parece-me que você tem feito tudo da ligação corretamente.
atualização: Que tal usar o NSArrayController
para adicionar itens para a matriz:
// 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
O problema parece ser que eu tinha sido vinculativo myViewArray
de MyView à propriedade arrangedObjects
do NSArrayController vez de sua propriedade content
.
Quando ligação a arrangedObjects
, descobri que o objeto real apontado por myViewArray
era uma instância de NSControllerArrayProxy
. Eu não encontrei uma resposta definitiva quanto ao que este objeto realmente faz quando eu procurou online para obter mais informações sobre ele. No entanto, os exemplos de código I encontrados sugerem que NSControllerArrayProxy é destinado para expor conveniências para acessar o propriedades de objetos na matriz , em vez dos objetos (na matriz)-se. É por isso que eu acredito que eu estava enganado na ligação a arrangedObjects
.
A solução foi, em vez ligam myViewArray
de MyView para content
propriedade do NSArrayController:
- (void)windowControllerDidLoadNib:(NSWindowController *) aController
{
[super windowControllerDidLoadNib:aController];
[myArrayController setContent:myArray];
[myView bind:@"myViewArray" toObject:myArrayController withKeyPath:@"content" options:nil];
}
Embora este parece funcionar, eu não estou 100% de certeza que é correto vincular a content
, neste caso. Se alguém pode lançar alguma luz sobre programaticamente ligação às várias propriedades de um NSArrayController, eu gostaria de receber comentários a esta resposta. Obrigado.
Em primeiro lugar, não há nada errado com a ligação a arrangedObjects: um NSTableColumn, por exemplo, deve ter seu conteúdo vinculado a apenas arrangedObjects, e seus contentValues ??para arrangedObjects.someProperty.
O erro comum é arrangedObjects consideram como o conteúdo de um arrayController mas que, como você viu, vai levar a dor: arrangedObjects é uma representação da forma como o arrayController tem atualmente dispostos os objetos em seu conteúdo, não o conteúdo em si.
Dito isto, a maneira de vincular uma matriz para um arrayController é:
[self.myArrayController bind:NSContentArrayBinding
toObject:self
withKeyPath:@"myView.myViewArray"
options:nil];
Você tem certeza, aliás, a sua visão precisa segurar o myViewArray? Isso geralmente cai sob a responsabilidade de um objeto controlador ou modelo.
Agora você pode adicionar objetos ao chamar addObject na arrayController , uma vez que é da responsabilidade do controlador.
[self.myArrayController addObject: anObject]