Доступ к коллекции через KVC (для защиты коллекции и соответствия KVO) - PullRequest
4 голосов
/ 09 февраля 2010

У меня есть класс Test, у которого есть массив Foos. Я хочу предоставить доступ к Foos, не подвергая ивару напрямую. Я пытаюсь сделать это KVC-совместимым (также проложить путь для соответствия KVO) У меня есть:

test.h

@interface Test : NSObject
{
    NSMutableArray *foos;
}

@property (readonly, copy) NSMutableArray *foos;

@end

Test.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

Что я делаю не так?

Ответы [ 3 ]

2 голосов
/ 10 февраля 2010
- (NSMutableArray*) foos
{
    return [self mutableArrayValueForKey:@"foos"];
}

Метод доступа не должен использовать KVC для получения значения свойства, к которому осуществляется доступ; идея состоит в том, что KVC проходит через средства доступа, потому что средства доступа ближе к значению, чем KVC.

Правильная реализация foos должна возвращать копию, изменяемую или нет, массива. Вот как я это сделаю:

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

Я бы также сделал все средства доступа открытыми. Все, что хочет изменить массив или получить произвольный доступ к элементам по определенным индексам, может сделать это таким образом. Это все еще безопасно и инкапсулировано, потому что они проходят через ваши методы доступа, а не напрямую обращаются к массиву.

Нет никакой причины использовать методы протокола KVC самостоятельно, если вы не знаете, к какому ключу вы будете обращаться во время написания кода. Например, если вы писали загрузчик пера или систему привязок Cocoa, вы бы использовали KVC.

1 голос
/ 09 февраля 2010

Проблема в том, что прокси-сервер NSMutableArray, возвращаемый mutableArrayValueForKey: сначала должен получить реальный массив, что он делает с помощью метода "foos". Так как это тот, который возвращает прокси NSMutableArray, он входит в бесконечный цикл. Одним из решений является использование другого имени:

- (NSMutableArray*) mutableFoos
{
    return [self mutableArrayValueForKey:@"foos"];
}
0 голосов
/ 20 февраля 2012

Я потратил очень много времени на эту проблему и хотел получить ее через средство доступа. Я хотел уточнить в ответ для тех, кто входит. Это то, что я сделал:

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

Тогда явно реализовано:

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

Хотя надо быть осторожным, getFoos представляется (недокументированным) средством доступа к KVC, поскольку это отправляет его в тот же цикл.

Затем на КВО:

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 *, а его подкласс, который может быть его причиной.

...