KVC를 통한 컬렉션 액세스 (수집을 보호하고 KVO 준수)
-
19-09-2019 - |
문제
푸스 배열이있는 클래스 테스트가 있습니다. IVAR을 직접 노출시키지 않고 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
내가 뭘 잘못하고 있죠?
해결책
- (NSMutableArray*) foos { return [self mutableArrayValueForKey:@"foos"]; }
액세서는 KVC를 사용하여 부동산의 값에 액세스하지 않아야합니다. 아이디어는 Accessor가 KVC보다 값에 더 가깝기 때문에 KVC는 액세서를 통과한다는 것입니다.
올바른 구현 foos
배열의 사본을 돌연변이하거나 다른 방법으로 반환해야합니다. 내가하는 방법은 다음과 같습니다.
- (NSArray *) foos
{
return [[foos copy] autorelease];
}
나는 또한 모든 액세서를 공개 할 것입니다. 배열을 돌연변이하거나 특정 인덱스에서 요소를 무작위로 액세스하려는 것은 그렇게 할 수 있습니다. 배열에 직접 액세스하지 않고 액세서를 겪고 있기 때문에 여전히 안전하고 캡슐화됩니다.
코드를 작성할 때 어떤 키에 액세스 할 키를 모르는 경우가 아니라면 KVC 프로토콜 메소드를 직접 사용해야 할 이유는 없습니다. 예를 들어, NIB 로더 또는 코코아 바인딩 시스템을 작성하는 경우 KVC를 사용합니다.
다른 팁
문제는 MutableArrayValueforKey에 의해 리턴 된 프록시 NSMutableARRAY : 먼저 "foos"메소드를 통해 실제 배열을 가져와야한다는 것입니다. 그것은 프록시 nsmutablearray를 반환하는 것이므로 무한 루프로 들어갑니다. 한 가지 해결책은 다른 이름을 사용하는 것입니다.
- (NSMutableArray*) mutableFoos
{
return [self mutableArrayValueForKey:@"foos"];
}
나는이 문제에 대해 아주 오랜 시간을 보냈고 액세서를 통해 이것을 얻고 싶었습니다. 나는 들어오는 사람들에 대한 답을 명확히하고 싶었습니다. 이것이 내가 한 일입니다.
@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를 발사하지 않습니다. 내 모험 어딘가에서, 나는 MutableArearrayvalueforkey에서 돌아온 것이 NSMutableArray*를 반환하지는 않지만 그 하위 클래스를 반환하는 것을 보았습니다.