Cocoa에서 NSMutableArray를 반복하고 특정 기준에 맞는 여러 객체를 제거하려면 객체를 제거 할 때마다 루프를 다시 시작하지 않고이를 수행하는 가장 좋은 방법은 무엇입니까?
감사,
편집 : 명확히하기 위해-가장 좋은 방법을 찾고있었습니다. 예를 들어 인덱스를 수동으로 업데이트하는 것보다 더 우아한 것입니다. 예를 들어 C ++에서는 할 수 있습니다.
iterator it = someList.begin();
while (it != someList.end())
{
if (shouldRemove(it))
it = someList.erase(it);
}
답변
명확히하기 위해 삭제할 항목을 수집하는 초기 루프를 만들고 싶습니다. 그런 다음 삭제합니다. Objective-C 2.0 구문을 사용한 샘플은 다음과 같습니다.
NSMutableArray *discardedItems = [NSMutableArray array];
for (SomeObjectClass *item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addObject:item];
}
[originalArrayOfItems removeObjectsInArray:discardedItems];
그런 다음 인덱스가 올바르게 업데이트되는지 또는 기타 작은 부기 세부 정보에 대해 의문의 여지가 없습니다.
추가하기 위해 편집 :
다른 답변에서는 역 공식화가 더 빨라야합니다. 즉, 배열을 반복하고 버릴 객체 대신 유지할 새 객체 배열을 작성하는 경우. 그것은 사실 일 수도 있지만 (새 배열을 할당하고 오래된 배열을 버리는 메모리와 처리 비용은 어떻습니까?) 더 빠르더라도 NSArrays 때문에 순진한 구현만큼 큰 문제는 아닙니다. “정상적인”배열처럼 행동하지 마십시오. 그들은 대화를하지만 다른 길을 걷습니다. 여기에 좋은 분석을보십시오 :
역 제형은 더 빠를 수 있지만, 위의 제형이 항상 내 요구에 충분히 빠르기 때문에 그것이인지 여부를 신경 쓸 필요가 없었습니다.
나를 위해 집으로가는 메시지는 당신에게 가장 분명한 공식을 사용하는 것입니다. 필요한 경우에만 최적화하십시오. 나는 개인적으로 위의 공식을 가장 명확하게 생각하기 때문에 그것을 사용합니다. 그러나 역 공식이 더 명확하다면 그것을 찾으십시오.
답변
한 가지 더 변형. 따라서 가독성과 성능이 우수합니다.
NSMutableIndexSet *discardedItems = [NSMutableIndexSet indexSet];
SomeObjectClass *item;
NSUInteger index = 0;
for (item in originalArrayOfItems) {
if ([item shouldBeDiscarded])
[discardedItems addIndex:index];
index++;
}
[originalArrayOfItems removeObjectsAtIndexes:discardedItems];
답변
이것은 매우 간단한 문제입니다. 당신은 거꾸로 반복 :
for (NSInteger i = array.count - 1; i >= 0; i--) {
ElementType* element = array[i];
if ([element shouldBeRemoved]) {
[array removeObjectAtIndex:i];
}
}
이것은 매우 일반적인 패턴입니다.
답변
다른 답변들 중 일부는 매우 큰 배열에서 성능이 떨어질 것 입니다. 수신기의 선형 검색을 수행하는 것과 같은 방법 removeObject:
과 removeObjectsInArray:
관련이있는 방법 은 객체의 위치를 이미 알고 있기 때문에 낭비입니다. 또한를 호출하면 removeObjectAtIndex:
한 번에 한 슬롯 씩 인덱스의 값을 배열 끝까지 복사해야합니다.
더 효율적인 것은 다음과 같습니다.
NSMutableArray *array = ...
NSMutableArray *itemsToKeep = [NSMutableArray arrayWithCapacity:[array count]];
for (id object in array) {
if (! shouldRemove(object)) {
[itemsToKeep addObject:object];
}
}
[array setArray:itemsToKeep];
의 용량을 설정 했으므로 itemsToKeep
크기 조정 중에 값을 복사하는 데 시간을 낭비하지 않습니다. 배열을 수정하지 않으므로 Fast Enumeration을 자유롭게 사용할 수 있습니다. 사용 setArray:
의 내용을 대체하기 array
로하는 것은 itemsToKeep
효율적입니다. 코드에 따라 마지막 줄을 다음과 같이 바꿀 수도 있습니다.
[array release];
array = [itemsToKeep retain];
따라서 값을 복사 할 필요가 없으며 포인터 만 교체하면됩니다.
답변
NSpredicate를 사용하여 변경 가능한 배열에서 항목을 제거 할 수 있습니다. for 루프가 필요하지 않습니다.
예를 들어 이름이 NSMutableArray 인 경우 다음과 같은 술어를 작성할 수 있습니다.
NSPredicate *caseInsensitiveBNames =
[NSPredicate predicateWithFormat:@"SELF beginswith[c] 'b'"];
다음 줄은 b로 시작하는 이름 만 포함 된 배열을 남깁니다.
[namesArray filterUsingPredicate:caseInsensitiveBNames];
필요한 술어를 작성하는 데 문제가있는 경우이 애플 개발자 링크를 사용 하십시오 .
답변
4 가지 방법으로 성능 테스트를 수행했습니다. 각 테스트는 100,000 개의 요소 배열에서 모든 요소를 반복하고 5 번째 항목마다 제거했습니다. 결과는 최적화 유무에 관계없이 크게 변하지 않았습니다. 이것은 iPad 4에서 수행되었습니다.
(1) removeObjectAtIndex:
– (271) MS
(2) removeObjectsAtIndexes:
– 1010 MS (인덱스 세트를 구축하는 것은 ~ 700 밀리 필요하기 때문에, 그렇지 않으면이 기본적으로 호출 removeObjectAtIndex과 동일 : 각 항목에 대한)
(3) removeObjects:
– (326) MS
(4) 테스트를 통과 한 객체로 새로운 배열을 만듭니다 -17ms
따라서 새 어레이를 만드는 것이 훨씬 빠릅니다. removeObjectsAtIndexes :를 사용하면 인덱스 세트를 빌드하는 데 필요한 시간 때문에 제거 할 항목이 많을수록 더 나빠진다는 점을 제외하고 다른 메소드는 모두 비슷합니다.
답변
인덱스에 대해 루프 카운트 다운을 사용하십시오.
for (NSInteger i = array.count - 1; i >= 0; --i) {
보관하려는 개체로 복사하십시오.
특히 for (id object in array)
루프 또는을 사용하지 마십시오 NSEnumerator
.