如何将对象添加到以编程方式绑定的NSMutableArray?

时间:2009-04-17 12:49:22

标签: objective-c cocoa cocoa-bindings key-value-observing key-value-coding

我有一个NSDocument,它具有以下结构:

@interface MyDocument : NSDocument
{
    NSMutableArray *myArray;

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

我在MyDocument.xib中实例化NSArrayController和MyView,并且已经建立了与文件所有者(MyDocument)的连接,因此我非常确定从Interface Builder的角度来看,我已经正确地完成了所有工作。 / p>

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时,它们似乎不会更新MyDocument的myArray。我尝试了以下两种方法:

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

(第二种方法导致编译器错误,如预期的那样,但我认为Objective-C运行时会根据我对KVO的有限理解“实现”此方法。)

我试图更新myViewArray的方式有问题吗?我的程序化绑定有问题吗? (我试图以编程方式执行此操作,因为MyView是一个自定义视图,我不想为它创建一个IB调色板。)

4 个答案:

答案 0 :(得分:1)

我可以在这里看到两种可能性:

首先,您是否在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

答案 1 :(得分:1)

问题似乎是我将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的各种属性,我欢迎对此答案的评论。感谢。

答案 2 :(得分:1)

问题是你是在直接改变你的数组。实施indexed accessor methods并调用它们。

KVO会覆盖您的访问者方法(只要您符合certain formats)并发布必要的通知。当你直接与阵列对话时,你不会得到这个;除非您明确告知,否则绑定到该属性的任何内容都不会知道您已更改了该属性。当您使用访问者方法时,KVO会告诉您其他对象。

不使用您的访问者方法(合成或其他方式)的唯一时间是initdealloc,因为您将与一半init ed或 - {{ 1}} ked对象。

一旦你使用自己的访问器方法来改变数组,从而获得免费的KVO通知,事情就应该有效:

  • 该视图在改变其属性时会自动通知数组控制器,该控制器会改变其dealloc属性,通知您的控制器。
  • 您的控制器在改变其属性时会自动通知数组控制器,该控制器会改变其content属性,该属性会通知视图。

答案 3 :(得分:1)

首先,绑定到arrangeObjects没有任何问题:例如,NSTableColumn应该只将其内容绑定到arrangeObjects,并将其contentValues绑定到arrangeObjects.someProperty。

常见的错误是将arrangeObjects视为arrayController的内容,但正如您所见,这将导致悲伤:arrangeObjects表示arrayController当前在其内容中排列对象的方式,而不是内容本身。

也就是说,将数组绑定到arrayController的方法是:

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

顺便说一下,你确定你的观点需要持有myViewArray吗?这通常由控制器或模型对象负责。

现在您可以通过调用arrayController 上的addObject 来添加对象,因为这是控制器的责任。

[self.myArrayController addObject: anObject]