enumerateObjectsUsingBlock과 for를 사용하는 경우


150

명백한 차이점 외에 :

  • enumerateObjectsUsingBlock인덱스와 객체가 모두 필요할 때 사용
  • enumerateObjectsUsingBlock지역 변수를 수정해야 할 때 사용하지 마십시오 (나는 이것에 대해 잘못했습니다. 범음의 대답을보십시오)

되어 enumerateObjectsUsingBlock일반적으로 고려 좋든 나쁘 때 for (id obj in myArray)것 또한 사용할 수 있습니까? 장점 / 단점은 무엇입니까 (예 : 성능이 다소 떨어짐)?


1
현재 색인이 필요한 경우 사용하고 싶습니다.
Besi September

답변:


350

궁극적으로 사용하려는 패턴을 사용하고 상황에 따라 자연스럽게 나오십시오.

for(... in ...)매우 편리하고 구문 적으로 간단 하지만 enumerateObjectsUsingBlock:흥미 롭거나 증명할 수없는 여러 가지 기능이 있습니다.

  • enumerateObjectsUsingBlock:빠른 열거보다 빠르거나 빠릅니다 ( 지원을 for(... in ...)사용하여 NSFastEnumeration열거 구현). 빠른 열거는 빠른 열거를 위해 내부 표현에서 표현으로 변환해야합니다. 오버 헤드가 있습니다. 블록 기반 열거는 콜렉션 클래스가 기본 스토리지 형식의 가장 빠른 순회만큼 빠르게 컨텐츠를 열거 할 수있게합니다. 배열과 관련이 없지만 사전에 큰 차이가있을 수 있습니다.

  • "로컬 변수를 수정해야 할 때 enumerateObjectsUsingBlock을 사용하지 마십시오"-사실이 아닙니다. 당신은 당신의 지역 주민을 선언 할 수 __block있으며 블록에서 쓸 수 있습니다.

  • enumerateObjectsWithOptions:usingBlock: 동시 또는 역 열거를 지원합니다.

  • 사전과 함께 블록 기반 열거는 키와 값을 동시에 검색하는 유일한 방법입니다.

개인적으로, 나는 개인의 선택 enumerateObjectsUsingBlock:보다 더 자주 사용 for (... in ...)합니다.


16
와우, 매우 유익합니다. 나는이 두 가지 대답을 모두 받아 들일 수 있기를 바랍니다.하지만 척과 함께 갈수록 더 좋아집니다. 또한 __block 을 검색하는 동안 귀하의 블로그 ( friday.com/bbum/2009/08/29/blocks-tips-tricks )를 찾았으며 더 많은 정보를 얻었습니다. 감사합니다.
Paul Wheeler 5

8
기록을 위해, 블록 기반 열거가 항상 "빠르거나 빠르지는 않다" mikeabdullah.net/slow-block-based-dictionary-enumeration.html
Mike Abdullah

2
@VanDuTran 블록은 별도의 스레드에서 실행되도록 지시하면 별도의 스레드에서만 실행됩니다. 당신이 열거 형의 동시성 옵션을 사용하지 않으면, 다음은 동일한 스레드에서 실행됩니다 호출되었다로
bbum

2
Nick Lockwood는 이것에 대해 정말 좋은 기사를 작성 enumerateObjectsUsingBlock했으며 배열 및 세트의 빠른 열거보다 여전히 상당히 느린 것 같습니다 . 왜 궁금해? iosdevelopertips.com/objective-c/…
Bob Spryn

2
그것은 구현 세부 사항의 종류이지만, 자동 enumerateObjectsUsingBlock호출 풀 (최소한 OS X 10.10)로 블록의 각 호출 을 래핑 하는 두 가지 접근 방식의 차이점 중이 답변에서 언급해야합니다 . 이것은 for in그렇지 않은 것과 비교하여 성능 차이를 설명합니다 .
Pol

83

간단한 열거의 경우 빠른 열거 (즉, for…in…루프)를 사용하는 것이 관용적 인 옵션입니다. 블록 방법은 약간 빠를 수 있지만 대부분의 경우 중요하지 않습니다. 소수의 프로그램은 CPU에 바인딩되어 있으며 내부 계산 대신 루프 자체가 병목 현상을 일으키는 경우는 거의 없습니다.

간단한 루프도 더 명확하게 읽습니다. 두 버전의 상용구는 다음과 같습니다.

for (id x in y){
}

[y enumerateObjectsUsingBlock:^(id x, NSUInteger index, BOOL *stop){
}];

색인을 추적하기 위해 변수를 추가하더라도 간단한 루프를 쉽게 읽을 수 있습니다.

그래서 언제 사용해야 enumerateObjectsUsingBlock:합니까? 나중에 또는 여러 곳에서 실행하기 위해 블록을 저장할 때. 실제로 루프 바디를 과도하게 대체하는 대신 블록을 일급 함수로 사용하는 경우에 좋습니다.


5
enumerateObjectsUsingBlock:모든 경우에 동일한 속도이거나 빠른 열거보다 빠릅니다. for(... in ...)내부 데이터 구조의 일부 중간 표현을 제공하기 위해 콜렉션이 필요한 빠른 열거를 사용합니다. 참고로, 관련이 없을 수 있습니다.
bbum

4
+1When you're storing a block to execute later or in multiple places. It's good for when you're actually using a block as a first-class function rather than an overwrought replacement for a loop body.
Steve

7
@bbum 내 자신의 테스트 enumerateObjects...는 실제로 루프를 사용하여 열거보다 느리게 열거 할 수 있음을 보여줍니다 . 이 테스트를 수천 번 실행했습니다. 블록과 루프의 본문은 동일한 한 줄의 코드 [(NSOperation *)obj cancel];였습니다. 평균 : 빠른 열거 루프- -[JHStatusBar dequeueStatusMessage:] [Line: 147] Fast enumeration time (for..in..loop): 0.000009및 블록- -[JHStatusBar dequeueStatusMessage:] [Line: 147] Enumeration time using block: 0.000043. 시차가 너무 크고 일관성이 있다는 것이 이상하지만 분명히 이것은 매우 구체적인 테스트 사례입니다.
chown

42

이 질문은 오래되었지만 내용이 변경되지 않았지만 허용 된 답변이 잘못되었습니다.

enumerateObjectsUsingBlockAPI는보다 우선 의미되지 않았다 for-in하지만 완전히 다른 사용 사례 :

  • 임의의 로컬이 아닌 논리를 적용 할 수 있습니다. 즉, 배열에서 블록을 사용하기 위해 블록이 무엇을하는지 알 필요가 없습니다.
  • 대규모 수집 또는 대량 계산을위한 동시 열거 ( withOptions:매개 변수 사용)

Fast Enumeration with with for-in는 여전히 컬렉션을 열거하는 관용적 방법입니다.

빠른 열거 형은 코드의 간결성, 가독성 및 추가 최적화 를 통해 자연스럽게 빠릅니다. 오래된 C for-loop보다 빠릅니다!

빠른 테스트에 따르면 iOS 7의 2014 년에 enumerateObjectsUsingBlockfor-in보다 700 % 느리게 (100 개 항목 배열의 1mm 반복을 기준으로) 느립니다.

여기서 성능이 실질적인 관심사입니까?

드물지만 예외는 아닙니다.

요점은 실제로 정당한 이유없이 enumerateObjectsUsingBlock:over 를 사용하면 이점이 거의 없음을 보여주는 것입니다 for-in. 코드를 더 읽기 쉽게 또는 더 빠르거나 스레드로부터 안전하게 만들지는 않습니다. (또 다른 일반적인 오해).

선택은 개인 취향에 달려 있습니다. 나에게 관용적이고 읽기 쉬운 옵션이 이긴다. 이 경우에는를 사용하는 고속 열거 for-in입니다.

기준:

NSMutableArray *arr = [NSMutableArray array];
for (int i = 0; i < 100; i++) {
    arr[i] = [NSString stringWithFormat:@"%d", i];
}
int i;
__block NSUInteger length;

i = 1000 * 1000;
uint64_t a1 = mach_absolute_time();
while (--i > 0) {
    for (NSString *s in arr) {
        length = s.length;
    }
}
NSLog(@"For-in %llu", mach_absolute_time()-a1);

i = 1000 * 1000;
uint64_t b1 = mach_absolute_time();
while (--i > 0) {
    [arr enumerateObjectsUsingBlock:^(NSString *s, NSUInteger idx, BOOL *stop) {
        length = s.length;
    }];
}
NSLog(@"Enum %llu", mach_absolute_time()-b1);

결과 :

2014-06-11 14:37:47.717 Test[57483:60b] For-in 1087754062
2014-06-11 14:37:55.492 Test[57483:60b] Enum   7775447746

4
MacBook Pro Retina 2014에서 동일한 테스트를 실행하는 enumerateObjectsUsingBlock것이 실제로 5 배 느리다는 것을 확인할 수 있습니다 . 블록의 각 호출을 래핑하는 오토 릴리즈 풀 때문일 수 있습니다 for in. 이 경우에는 발생하지 않습니다 .
Pol

2
enumerateObjectsUsingBlock:Xcode 7.x를 사용하여 실제 iPhone 6 iOS9에서 여전히 4 배 느리다는 것을 확인 합니다.
Cœur

1
확인해 주셔서 감사합니다! 나는이 답변이 그렇게 묻히지 않기를 바랍니다 ... 어떤 사람들 enumerate은 FP와 같은 느낌이 들고 성능 분석을 듣고 싶지 않기 때문에 구문을 좋아합니다 .
Adam Kaplan

1
그건 그렇고, 그것은 단지 자동 릴리스 풀 때문이 아닙니다. enumerate:
Adam Kaplan

24

성능에 관한 질문에 답하기 위해 성능 테스트 프로젝트를 사용하여 몇 가지 테스트를 수행했습니다 . 배열의 모든 객체에 메시지를 보내는 세 가지 옵션 중 가장 빠른 옵션을 알고 싶었습니다.

옵션은 다음과 같습니다.

1) makeObjectsPerformSelector

[arr makeObjectsPerformSelector:@selector(_stubMethod)];

2) 빠른 열거 및 일반 메시지 보내기

for (id item in arr)
{
    [item _stubMethod];
}

3) enumerateObjectsUsingBlock 및 일반 메시지 전송

[arr enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) 
 {
     [obj _stubMethod];
 }];

makeObjectsPerformSelector가 가장 느리다는 것이 밝혀졌습니다. 빠른 열거보다 두 배가 걸렸습니다. enumerateObjectsUsingBlock이 가장 빠르며 빠른 반복보다 약 15-20 % 빠릅니다.

따라서 최상의 성능에 대해 매우 우려되는 경우 enumerateObjectsUsingBlock을 사용하십시오. 그러나 어떤 경우에는 컬렉션을 열거하는 데 걸리는 시간이 각 객체가 실행하기를 원하는 코드를 실행하는 데 걸리는 시간에 비해 뒤쳐집니다.


특정 시험을 지적 할 수 있습니까? 당신은 잘못된 대답을 한 것 같습니다.
Cœur

3

중첩 루프를 중단하려는 경우 enumerateObjectsUsingBlock을 외부 루프로 사용하는 것이 매우 유용합니다.

예 :

[array1 enumerateObjectsUsingBlock:^(id obj1, NSUInteger idx, BOOL * _Nonnull stop) {
  for(id obj2 in array2) {
    for(id obj3 in array3) {
      if(condition) {
        // break ALL the loops!
        *stop = YES;
        return;
      }
    }
  }
}];

대안은 goto 문을 사용하는 것입니다.


1
또는 당신은 당신이 D는 여기에했던 것처럼 메소드로부터 반환 할 수
아담 카플란

1

성능에 대한 포괄적 인 비교를 시작한 @bbum 및 @Chuck에게 감사합니다. 사소한 일임을 알게되어 기쁘다. 나는 함께 갔다 :

  • for (... in ...)-내 기본 이동으로. IDE 자동 완성으로 인해 대부분의 데이터 구조에 대한 타이핑이 적습니다.

  • enumerateObject...-객체 및 색인에 대한 액세스가 필요한 경우 비 배열 또는 사전 구조에 액세스 할 때 (개인 선호도)

  • for (int i=idx; i<count; i++) -배열이 아닌 0이 아닌 인덱스에서 시작해야 할 때

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.