【问题标题】:Accessing collection through KVC (to protect collection and be KVO compliant)通过 KVC 访问集合(以保护集合并符合 KVO)
【发布时间】:2010-02-09 15:56:51
【问题描述】:

我有一个类 Test,它有一个 Foos 数组。我想在不直接暴露 ivar 的情况下提供对 Foos 的访问。我正在尝试使此 KVC 兼容(也为 KVO 兼容铺平道路)。我有:

测试.h

@interface Test : NSObject
{
    NSMutableArray *foos;
}

@property (readonly, copy) NSMutableArray *foos;

@end

测试.m

- (id) init
{
    self = [super init];
    if (self != nil)
    {
        foos = [[NSMutableArray array] retain];
    }
    return self;
}

- (NSMutableArray*) foos
{
    return [self mutableArrayValueForKey:@"foos"];
}

- (NSUInteger)countOfFoos
{
    return [foos count];
}

- (id)objectInFoosAtIndex:(NSUInteger)index
{
    return [foos objectAtIndex:index];
}

- (NSArray *)foosAtIndexes:(NSIndexSet *)indexes
{
    return [foos objectsAtIndexes:indexes];
}

- (void)insertObject:(id)key inFoosAtIndex:(NSUInteger)index
{
    [foos insertObject:key atIndex:index];
}

- (void)insertFoos:(NSArray *)foosArray atIndexes:(NSIndexSet *)indexes
{
    [foos insertObjects:foosArray atIndexes:indexes];
}

- (void)removeObjectFromFoosAtIndex:(NSUInteger)index
{
    [foos removeObjectAtIndex:index];
}

- (void)removeFoosAtIndexes:(NSIndexSet *)indexes
{
    [foos removeObjectsAtIndexes:indexes];
}

当客户端尝试添加 Foo 时,这会进入无限循环:

Test *test = [[Test alloc] init];
NSMutableArray *foos = test.foos;
[foos addObject:@"adding object"]; // infinite loop here

我做错了什么?

【问题讨论】:

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


    【解决方案1】:
    - (NSMutableArray*) foos
    {
        return [self mutableArrayValueForKey:@"foos"];
    }
    

    访问器不应该使用 KVC 来获取被访问属性的值;这个想法是 KVC 通过访问器,因为访问器比 KVC 更接近值。

    foos 的正确实现应该返回数组的一个副本,无论是可变的还是其他的。这是我的做法:

    - (NSArray *) foos
    {
        return [[foos copy] autorelease];
    }
    

    我还将公开所有访问者。任何想要改变数组或随机访问特定索引处的元素的东西都可以这样做。它仍然是安全和封装的,因为它们通过您的访问器,而不是直接访问数组。

    实际上没有任何理由自己使用 KVC 协议方法,除非您在编写代码时不知道要访问什么密钥。例如,如果您正在编写 nib 加载程序或 Cocoa Bindings 系统,您将使用 KVC。

    【讨论】:

    • Peter,你说的对我来说很有道理,但据我了解,如果“-foos”返回一个副本,我将无法在 Test.foos 上使用 KVO。在不同命名的方法中使用“mutableArrayValueForKey:”(请参阅​​我自己的答案)解决了这个问题,因为代理 NSMutableArray 然后通过测试 KVC 兼容方法。有cmets吗?谢谢!
    • “……如果“-foos”返回一个副本,我将无法在 Test.foos 上使用 KVO。”是的,你会的。 KVO 通知由(KVO 代表)您的班级发布,而不是由您的 NSMutableArray 发布。 mutableArrayValueForKey: 是一个 KVO 方法,它创建一个代理数组,将访问者消息发送到您的对象,反过来会导致 KVO 通知;它不仅仅是您的 NSMutableArray 翻转了 KVO 开关。观察员观察您的财产;他们没有观察到它的任何特定数组值。只要您使用 setter 和 mutator 方法,返回副本的 getter 就可以正常工作。
    • 我的代码正在做的是获取 foos 数组并直接操作它(没有测试的 foo 访问器方法)。我的意思是,如果 Test 返回一个普通的 foos 数组(副本或原始数组),那么 KVO 将不起作用,而使用 mutableArrayValueForKey 返回的代理 NSMutableArray 它将起作用,因为代理调用访问器。使用 mutableArrayValueForKey 错了吗?我应该明确使用 Test mutator 方法吗?
    • 您应该直接使用 mutator 访问器,因为它有效并且是最干净的代码。第二种方法是自己调用mutableArrayValueForKey:,而不是让其中一个访问者调用它。这样,在调用站点中就明确表明您正在使用代理数组以 KVC 方式执行操作。但是,访问器无需通过代理即可提供相同的好处,而且很明显,只要访问器以正确的模式命名,您就符合 KVC。
    【解决方案2】:

    问题是 mutableArrayValueForKey: 返回的代理 NSMutableArray 首先必须获取真正的数组,它通过“foos”方法完成。由于那是返回代理 NSMutableArray 的那个,它进入了一个无限循环。一种解决方案是使用另一个名称:

    - (NSMutableArray*) mutableFoos
    {
        return [self mutableArrayValueForKey:@"foos"];
    }
    

    【讨论】:

      【解决方案3】:

      我在这个问题上花了很长时间,想通过访问器来解决这个问题。我想为那些进来的人澄清答案。这就是我所做的:

      @property (nonatomic,readonly,getter=getTheFoos) NSMutableArray* foos;
      

      那么显然实现了:

      - (NSMutableArray*)getTheFoos {
          return [self mutableArrayValueForKey:@"foos"];
      }
      

      但必须小心,getFoos 似乎是一个(未记录的)KVC 访问器,因为这会将其发送到同一个循环中。

      然后进入KVO:

      Test* test= [[Test alloc] init];
      NSObject* obj= [[NSObject alloc] init];
      NSMutableArray* arrTheData= test.foos;
      
      [test.foos insertObject:obj atIndex:0];
      [arrFoos insertObject:obj atIndex:0];
      

      arrFoos 可以读取更新后的变异数组(其中将包含两个对象),但插入其中不会触发 KVO。在我冒险的某个地方,我看到 mutableArrayValueForKey: 返回的不是 NSMutableArray*,而是它的子类,这可能是它的原因。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2010-09-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-17
        • 2017-03-14
        相关资源
        最近更新 更多