NSArray를 어떻게 반복합니까?


답변:


667

10.5 + / iOS에 일반적으로 선호되는 코드입니다.

for (id object in array) {
    // do something with object
}

이 구문은 NSFastEnumeration프로토콜 을 따르는 컬렉션의 개체를 열거하는 데 사용됩니다 . 이 방법은 단일 객체 호출을 통해 얻은 여러 객체에 대한 포인터를 버퍼에 저장하고 포인터 산술을 사용하여 버퍼를 통해 진행하여 객체를 반복하므로 속도 이점이 있습니다. 이다 훨씬 빠른 호출하는 것보다 -objectAtIndex:루프를 때마다.

그것은 당신이 기술적 중에 있음을 주목할 가치가 있습니다 사용 A에 대한-에를 단계별로 루프 NSEnumerator, 내가 찾은 그이 무효화합니다 거의 모든의 빠른 열거의 속도의 장점. 그 이유는 기본 NSEnumerator구현이 -countByEnumeratingWithState:objects:count:각 호출에서 버퍼에 하나의 객체 만 배치 하기 때문입니다 .

나는 이것을 radar://6296108(NSEnumerators의 빠른 열거가 느리다) 에서보고 했지만 고치지 않음으로 반환되었습니다. 그 이유는 빠른 열거가 개체 그룹을 미리 가져오고 열거 자의 주어진 지점으로 만 열거하려는 경우 (예 : 특정 개체가 발견되거나 조건이 충족 될 때까지) 분류 후 동일한 열거자를 사용하기 때문입니다. 루프의 경우 여러 개체를 건너 뛰는 경우가 종종 있습니다.

OS X 10.6 / iOS 4.0 이상을 코딩하는 경우 블록 기반 API를 사용하여 배열 및 기타 컬렉션을 열거 할 수도 있습니다.

[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
    // do something with object
}];

당신은 또한 사용할 수 있습니다 -enumerateObjectsWithOptions:usingBlock:및 통과 NSEnumerationConcurrent및 / 또는 NSEnumerationReverse옵션 인수로.


10.4 이하

10.5 이전의 표준 관용구는 다음 NSEnumerator과 같이 and 루프 를 사용하는 것입니다.

NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
  // do something with object
}

간단하게 유지하는 것이 좋습니다. 배열 유형에 자신을 묶는 것은 융통성이 없으며 사용의 속도 향상은 -objectAtIndex:어쨌든 10.5 이상에서 빠른 열거로 개선에 중요하지 않습니다. (빠른 열거는 실제로 기본 데이터 구조에서 포인터 산술을 사용하고 대부분의 메소드 호출 오버 헤드를 제거합니다.) 조기 최적화는 결코 좋은 생각이 아닙니다. 어쨌든 병목 현상이 아닌 문제를 해결하기 위해 더 복잡한 코드가 생성됩니다.

를 사용하면 다른 코드 변경없이 -objectEnumerator열거 가능한 다른 컬렉션 ( NSSet, 키의 NSDictionary등) 으로 쉽게 변경 하거나 -reverseObjectEnumerator배열을 뒤로 열거하도록 전환 할 수도 있습니다 . 반복 코드가 메소드에있는 경우 어떤 것도 전달할 수 NSEnumerator있으며 코드는 반복되는 것을 신경 쓰지 않아도됩니다 . 또한 NSEnumerator(적어도 Apple 코드에서 제공하는 것)은 더 많은 객체가있는 한 열거하는 컬렉션을 유지하므로 자동 릴리즈 된 객체가 얼마나 오래 존재할지 걱정할 필요가 없습니다.

아마도 NSEnumerator(또는 빠른 열거)가 당신을 보호 하는 가장 큰 것은 열거 할 때 지식 없이도 변경 가능한 컬렉션 (배열 또는 기타) 변경을 갖는 것입니다. 인덱스로 개체에 액세스하는 경우 디버깅이 끔찍할 수있는 이상한 예외 또는 일대일 오류 (종종 문제가 발생한 후)가 발생할 수 있습니다. 표준 관용구 중 하나를 사용하는 열거 형에는 "실패한"동작이 있으므로 돌연변이가 발생한 후 다음 객체에 액세스하려고하면 문제 (잘못된 코드로 인해)가 즉시 나타납니다. 프로그램이 점점 복잡해지고 멀티 스레딩되거나 타사 코드가 수정할 수있는 것에 의존함에 따라 취약한 열거 코드는 점점 문제가되고 있습니다. 캡슐화와 추상화 FTW! :-)



28
참고 : 대부분의 컴파일러는 "while (object = [e nextObject])"에 대한 경고를 표시합니다. 이 경우 실제로 == 대신 =를 사용하는 것을 의미합니다. 경고를 표시하지 않으려면 "while ((object = [e nextObject]))"와 같이 괄호를 추가 할 수 있습니다.
Adam Rosenfield 2016 년

NSEnumerator에서 기억해야 할 또 다른 사항은 objectWithIndex 방식보다 더 많은 메모리를 사용 (배열의 사본을 작성)한다는 것입니다.
diederikh 2018 년

@QuinnTaylor를 사용 for (id object in array)하면 배열에서 객체의 현재 색인을 결정하는 방법이 있습니까? 아니면 별도의 카운터를 포함해야합니까?
Coderama

1
별도의 카운터가 필요하지만 블록 기반 열거 형 (인덱스 포함)을 사용할 수 있다면 고려하는 것이 좋습니다.
Quinn Taylor

나는 이상하지만, for다음과 같은 루프를 사용합니다 :for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
nielsbot

125

OS X 10.4.x 및 이전 버전의 경우 :

 int i;
 for (i = 0; i < [myArray count]; i++) {
   id myArrayElement = [myArray objectAtIndex:i];
   ...do something useful with myArrayElement
 }

OS X 10.5.x (또는 iPhone) 이상 :

for (id myArrayElement in myArray) {
   ...do something useful with myArrayElement
}

2
첫 번째 예에서는 루프 외부에서 int를 선언 할 필요조차 없습니다. 이것은 잘 작동하며 변수의 범위를 잘 지정하여 필요한 경우 나중에 재사용 할 수 있습니다. 배열을 통해 -objectAtIndex 사용의 이점을 취소 할 수 있습니다.
Quinn Taylor

1
실제로 변경 가능한 컬렉션을 열거하는 경우 루프를 통해 매번 카운트를 확인하는 것이 기술적으로 더 정확합니다. NSEnumerator 또는 NSFastEnumeration을 사용하면 배열의 동시 돌연변이로부터 보호 할 수 있다고 설명하기 위해 내 대답을 명확히했습니다.
Quinn Taylor

for (int i = 0; ...) 구성은 C 언어 방언입니다 (C99는 믿습니다). 내가 직접 사용하지만 XCode 기본값인지 확실하지 않았습니다.
diederikh 2018 년

C99는 Xcode 3.1.x까지의 기본값입니다. 나중에 언젠가는 기본값이 GNU99로 변경되어 익명의 공용체와 구조체를 지원합니다. 그건 좋을
거야

2
이 방법을 사용 for (NSUInteger i = 0, count = [myArray count]; i < count; i++)하면 가장 효율적이고 간결하게 사용할 수 있습니다.
퀸 테일러

17

테스트 및 소스 코드의 결과는 다음과 같습니다 (앱에서 반복 횟수를 설정할 수 있음). 시간은 밀리 초이며 각 항목은 테스트를 5-10 회 실행 한 평균 결과입니다. 나는 일반적으로 유효 숫자 2-3 자리까지 정확하고 그 후에는 매 실행마다 다를 수 있음을 발견했다. 이는 1 % 미만의 오차 한계를 제공합니다. 내가 관심있는 대상 플랫폼이기 때문에 테스트는 iPhone 3G에서 실행되었습니다.

numberOfItems   NSArray (ms)    C Array (ms)    Ratio
100             0.39            0.0025          156
191             0.61            0.0028          218
3,256           12.5            0.026           481
4,789           16              0.037           432
6,794           21              0.050           420
10,919          36              0.081           444
19,731          64              0.15            427
22,030          75              0.162           463
32,758          109             0.24            454
77,969          258             0.57            453
100,000         390             0.73            534

데이터 세트 (NSDictionary, NSArray, NSSet 등)를 처리하기 위해 Cocoa가 제공하는 클래스는 메모리 관리, 재 할당 등의 관료주의에 대해 걱정할 필요없이 정보 관리를위한 매우 훌륭한 인터페이스를 제공합니다. 물론 비용이 많이 듭니다. . NSNumbers의 NSArray를 사용하는 것이 간단한 반복을 위해 Float의 C Array보다 느리다는 것이 명백하다고 생각합니다. 그래서 몇 가지 테스트를하기로 결정했으며 결과는 매우 충격적이었습니다! 나는 이것이 나쁜 것이라고 기대하지 않았습니다. 참고 :이 테스트는 내가 관심있는 대상 플랫폼이므로 iPhone 3G에서 수행됩니다.

이 테스트에서는 C float *와 NSNumber의 NSArray간에 매우 간단한 랜덤 액세스 성능 비교를 수행합니다.

각 배열의 내용을 요약하고 mach_absolute_time ()을 사용하여 시간을 지정하는 간단한 루프를 만듭니다. NSMutableArray는 평균 400 배 더 오래 걸립니다 !! (400 %가 아니라 400 배 더 길다. 40,000 % 더 길다!).

헤더:

// Array_Speed_TestViewController.h

// 배열 속도 테스트

// Mehmet Akten이 2009 년 5 월 2 일에 작성 함.

// Copyright MSA Visuals Ltd. 2009. 모든 권리 보유.

#import <UIKit/UIKit.h>

@interface Array_Speed_TestViewController : UIViewController {

    int                     numberOfItems;          // number of items in array

    float                   *cArray;                // normal c array

    NSMutableArray          *nsArray;               // ns array

    double                  machTimerMillisMult;    // multiplier to convert mach_absolute_time() to milliseconds



    IBOutlet    UISlider    *sliderCount;

    IBOutlet    UILabel     *labelCount;


    IBOutlet    UILabel     *labelResults;

}


-(IBAction) doNSArray:(id)sender;

-(IBAction) doCArray:(id)sender;

-(IBAction) sliderChanged:(id)sender;


@end

이행:

// Array_Speed_TestViewController.m

// 배열 속도 테스트

// Mehmet Akten이 2009 년 5 월 2 일에 작성 함.

// Copyright MSA Visuals Ltd. 2009. 모든 권리 보유.

    #import "Array_Speed_TestViewController.h"
    #include <mach/mach.h>
    #include <mach/mach_time.h>

 @implementation Array_Speed_TestViewController



 // Implement viewDidLoad to do additional setup after loading the view, typically from a nib.

- (void)viewDidLoad {

    NSLog(@"viewDidLoad");


    [super viewDidLoad];


    cArray      = NULL;

    nsArray     = NULL;


    // read initial slider value setup accordingly

    [self sliderChanged:sliderCount];


    // get mach timer unit size and calculater millisecond factor

    mach_timebase_info_data_t info;

    mach_timebase_info(&info);

    machTimerMillisMult = (double)info.numer / ((double)info.denom * 1000000.0);

    NSLog(@"machTimerMillisMult = %f", machTimerMillisMult);

}



// pass in results of mach_absolute_time()

// this converts to milliseconds and outputs to the label

-(void)displayResult:(uint64_t)duration {

    double millis = duration * machTimerMillisMult;


    NSLog(@"displayResult: %f milliseconds", millis);


    NSString *str = [[NSString alloc] initWithFormat:@"%f milliseconds", millis];

    [labelResults setText:str];

    [str release];

}




// process using NSArray

-(IBAction) doNSArray:(id)sender {

    NSLog(@"doNSArray: %@", sender);


    uint64_t startTime = mach_absolute_time();

    float total = 0;

    for(int i=0; i<numberOfItems; i++) {

        total += [[nsArray objectAtIndex:i] floatValue];

    }

    [self displayResult:mach_absolute_time() - startTime];

}




// process using C Array

-(IBAction) doCArray:(id)sender {

    NSLog(@"doCArray: %@", sender);


    uint64_t start = mach_absolute_time();

    float total = 0;

    for(int i=0; i<numberOfItems; i++) {

        total += cArray[i];

    }

    [self displayResult:mach_absolute_time() - start];

}



// allocate NSArray and C Array 

-(void) allocateArrays {

    NSLog(@"allocateArrays");


    // allocate c array

    if(cArray) delete cArray;

    cArray = new float[numberOfItems];


    // allocate NSArray

    [nsArray release];

    nsArray = [[NSMutableArray alloc] initWithCapacity:numberOfItems];



    // fill with random values

    for(int i=0; i<numberOfItems; i++) {

        // add number to c array

        cArray[i] = random() * 1.0f/(RAND_MAX+1);


        // add number to NSArray

        NSNumber *number = [[NSNumber alloc] initWithFloat:cArray[i]];

        [nsArray addObject:number];

        [number release];

    }


}



// callback for when slider is changed

-(IBAction) sliderChanged:(id)sender {

    numberOfItems = sliderCount.value;

    NSLog(@"sliderChanged: %@, %i", sender, numberOfItems);


    NSString *str = [[NSString alloc] initWithFormat:@"%i items", numberOfItems];

    [labelCount setText:str];

    [str release];


    [self allocateArrays];

}



//cleanup

- (void)dealloc {

    [nsArray release];

    if(cArray) delete cArray;


    [super dealloc];

}


@end

보낸 사람 : memo.tv

//////////////////////

블록이 도입 된 이후에 사용 가능하므로 블록이있는 배열을 반복 할 수 있습니다. 구문은 빠른 열거만큼 좋지는 않지만 동시 열거라는 매우 흥미로운 기능이 있습니다. 열거 순서가 중요하지 않고 잠금없이 작업을 병렬로 수행 할 수 있으면 다중 코어 시스템에서 상당한 속도 향상을 제공 할 수 있습니다. 동시 열거 섹션에서 이에 대해 자세히 알아보십시오.

[myArray enumerateObjectsUsingBlock:^(id object, NSUInteger index, BOOL *stop) {
    [self doSomethingWith:object];
}];
[myArray enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    [self doSomethingWith:object];
}];

/////////// NSFastEnumerator

빠른 열거의 개념은 빠른 C 배열 액세스를 사용하여 반복을 최적화하는 것입니다. 기존 NSEnumerator보다 빠를뿐만 아니라 Objective-C 2.0도 매우 간결한 구문을 제공합니다.

id object;
for (object in myArray) {
    [self doSomethingWith:object];
}

//////////////////

NSEnumerator

이것은 외부 반복 형식입니다. [myArray objectEnumerator]는 객체를 반환합니다. 이 객체에는 nextObject 메소드가 있는데, nil을 반환 할 때까지 루프에서 호출 할 수 있습니다.

NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
    [self doSomethingWith:object];
}

//////////////////

objectAtIndex : 열거

정수를 증가시키는 for 루프를 사용하고 [myArray objectAtIndex : index]를 사용하여 객체를 쿼리하는 것이 가장 기본적인 열거 형입니다.

NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
    [self doSomethingWith:[myArray objectAtIndex:index]];
}

/////////////// 보낸 사람 : darkdust.net


12

세 가지 방법은 다음과 같습니다.

        //NSArray
    NSArray *arrData = @[@1,@2,@3,@4];

    // 1.Classical
    for (int i=0; i< [arrData count]; i++){
        NSLog(@"[%d]:%@",i,arrData[i]);
    }

    // 2.Fast iteration
    for (id element in arrData){
        NSLog(@"%@",element);
    }

    // 3.Blocks
    [arrData enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
         NSLog(@"[%lu]:%@",idx,obj);
         // Set stop to YES in case you want to break the iteration
    }];
  1. 가장 빠른 실행 방법이며 3. 자동 완성 기능을 사용하면 반복 범위 작성을 잊어 버립니다.

7

each메소드를 추가 NSArray category하면 많이 필요할 것입니다.

ObjectiveSugar 에서 가져온 코드

- (void)each:(void (^)(id object))block {
    [self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        block(obj);
    }];
}

5

다음은 문자열 배열을 선언하고 반복하는 방법입니다.

NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];

for (int i = 0; i < [langs count]; i++) {
  NSString *lang = (NSString*) [langs objectAtIndex:i];
  NSLog(@"%@, ",lang);
}

0

스위프트

let arrayNumbers = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

// 1
for (index, value) in arrayNumbers.enumerated() {
    print(index, value)
    //... do somthing with array value and index
}


//2
for value in arrayNumbers {
    print(value)
    //... do somthing with array value
}

-1

이 작업을 수행 :-

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