【问题标题】:How to add an object to a programmatically bound NSMutableArray?如何将对象添加到以编程方式绑定的 NSMutableArray?
【发布时间】:2009-04-17 12:49:22
【问题描述】:

我有一个具有以下结构的 NSDocument:

@interface MyDocument : NSDocument
{
    NSMutableArray *myArray;

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

我在 MyDocument.xib 中实例化了 NSArrayController 和 MyView,并与文件的所有者 (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 时,它们似乎不会更新 MyDocument 的 myArray。我尝试了以下两种方法:

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

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

    唯一不使用您的访问器方法(综合或其他)的时间是在initdealloc,因为您将与一个半inited 或-deallocked 对象交谈。

    一旦您使用自己的访问器方法来改变数组,从而获得免费的 KVO 通知,一切就应该正常工作了:

    • 视图在改变其属性时会自动通知数组控制器,数组控制器会改变其content 属性,从而通知您的控制器。
    • 您的控制器在改变其属性时,会自动通知数组控制器,后者会改变其arrangedObjects 属性,从而通知视图。

    【讨论】:

    • 出于某种原因,我认为阵列控制器有能力监听阵列本身的变化。我可能已经完全错误地处理了这件事,因为(正如我回复您对我的回答的评论),即使我将 insertObject:inMyViewArrayAtIndex: 添加到 MyView,并尝试在该方法中将 NSValue 插入 myViewArray 并调用mouseUp 中的那个方法:,它只有在我将 myViewArray 绑定到 MyDocument 中的 NSArrayController.content 时才有效。安排对象代理失败。
    • “出于某种原因,我认为阵列控制器有能力监听阵列本身的变化。”这是KVO。数组控制器将自己添加为 bound-to 属性的观察者;当您使用其访问器更改该属性时,KVO 会发布适当的通知,数组控制器会收到这些通知。
    • 好的,很好,因为这就是我认为正在发生的事情。感谢您清除它。与此同时,我仍在试图弄清楚如何将 NSArrayController 与 NSValue 元素数组一起使用,如果我们应该绑定到排列对象(这似乎不会直接暴露元素本身)。跨度>
    • NSValue 对象是原始对象(包装结构和数字),而不是模型对象。上一层并创建一个真正的模型类,并将其实例放入数组中。示例:如果这些是矩形,您可以创建一个 Rectangle 类,该类还具有填充、描边等属性。您还可以不同地说明矩形本身:例如,作为四个(可绑定)角点而不是一个角和一个大小。
    【解决方案2】:

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

    首先,您是否在您的MyDocument 类中实例化NSMutableArray 对象(并释放它)?它应该看起来像这样:

    - (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
        

        【讨论】:

        • 是的,我没有将它包含在代码 sn-ps 中,但我在 init 方法中分配并保留了一个 NSMutableArray。我没有将 MyView 的 myViewArray 变量设置为属性 - 我会尝试这样做,但为了完成,我假设如果 myViewArray 只是一个直接的实例变量,这也应该有效?
        • 我很确定 myViewArray 必须是一个属性。 cocoa 绑定系统严重依赖通知系统,@synthesized 属性自动实现必要的通知。
        • 嗯,我完全按照您推荐的语法添加了该属性,但它似乎没有达到我希望的效果。我假设,根据您的代码,属性和实例变量之间没有命名空间冲突? (ISTR 默认属性使用同名的实例变量,所以不应该。)
        • 是的,没错。如果您指定一个与实例变量同名的@property,则该实例变量将用于该属性。
        • 又想到了一个想法。您是否尝试过使用 NSArrayController 将项目添加到数组中?我有一种感觉,这将是做事的正确方式。
        【解决方案3】:

        问题似乎是我一直将 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 的各种属性有所了解,我会欢迎 cmets 回答这个问题。谢谢。

        【讨论】:

        • 不是。您应该绑定到排列对象。
        • 当我将绑定更改为排列对象而不是内容时(即使我实现了您在答案中建议的索引访问器方法),NSArrayController 将 MyView 的 myViewArray 设置为不响应insertObject:atIndex: 我从索引访问器方法调用的方法:2009-04-18 07:20:17.670 MyProj[37032:10b] *** -[_NSControllerArrayProxy insertObject:atIndex:]: 无法识别的选择器发送到实例 0x16dea0 (错误信息被打印两次。)
        • 我继续在网上查找支持我的(可能不正确的)理论的代码示例,即“arrangedObjects”不打算直接绑定到。相反,它是一个有用的代理对象,适用于您想要引用数组中某个项目的属性的情况。所有示例都显示了与“arrangedObject.someProperty”的绑定,但绝不只是与“arrangedObjects”本身的绑定。
        • 是的,您确实需要绑定到数组中模型对象的属性。尽管如此,arrangedObjects 是要绑定到的正确数组。尝试向模型对象添加自属性,其中 getter 仅返回 self 而 setter 的工作方式类似于 -[NSMutableString setString:] (内容的就地替换)。您需要遵循相同的命名模式:如果您的模型类是 MyThing,则 getter 是 -thing,setter 是 -setThing:。
        • 模型对象是 NSValue 实例,每个实例都简单地包装了一个 NSRect。据我所知,没有任何属性可以轻松附加到 @"arrangedObjects" 键路径中,以提供我真正关心的 NSValue(即 NSRect)中的数据。因此,我需要一些方法来获取排列对象的实际元素。不是吗?
        【解决方案4】:

        首先,绑定到排列对象没有任何问题:例如,一个 NSTableColumn 应该将其内容仅绑定到排列对象,并将其内容值绑定到排列对象.someProperty。

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

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

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

        顺便说一句,您确定您的视图需要保存 myViewArray 吗?这通常由控制器或模型对象负责。

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

        [self.myArrayController addObject: anObject]
        

        【讨论】:

        • 视图需要保存数组才能显示。但正如您所展示的,他没有将数组控制器的内容绑定到视图;他正在将其绑定到文档。
        • 幸运的是,视图不需要为了显示它而持有一个对象——如果他们这样做了,它会使 MVC 模式无用。严格来说,视图不显示数组,它,或者更可能是它的子视图之一,显示 arrayController 的排列对象。我不确定您将 arrayController 的内容绑定到文档是什么意思:Quinn Taylor 的代码和我的代码都只在 arrayController 和数组之间建立了绑定。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-05-14
        • 2012-09-27
        • 1970-01-01
        • 1970-01-01
        • 2012-02-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多