Làm cách nào để lặp lại NSArray?


Câu trả lời:


667

Mã thường được ưu tiên cho 10,5 + / iOS.

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

Cấu trúc này được sử dụng để liệt kê các đối tượng trong một bộ sưu tập phù hợp với NSFastEnumerationgiao thức. Cách tiếp cận này có lợi thế về tốc độ vì nó lưu trữ các con trỏ tới một số đối tượng (thu được thông qua một lệnh gọi phương thức) trong một bộ đệm và lặp qua chúng bằng cách tiến qua bộ đệm bằng số học con trỏ. Đây là nhiều nhanh hơn gọi -objectAtIndex:mỗi lần thông qua các vòng lặp.

Cũng đáng lưu ý rằng về mặt kỹ thuật bạn có thể sử dụng vòng lặp for để bước qua một NSEnumerator, tôi đã thấy rằng điều này vô hiệu hóa hầu như tất cả lợi thế về tốc độ của phép liệt kê nhanh. Lý do là việc NSEnumeratortriển khai mặc định -countByEnumeratingWithState:objects:count:chỉ đặt một đối tượng trong bộ đệm trên mỗi cuộc gọi.

Tôi đã báo cáo điều này trong radar://6296108 (Bảng liệt kê nhanh của NSEnumerators là chậm chạp) nhưng nó đã được trả lại là Không được sửa. Lý do là việc liệt kê nhanh lấy trước một nhóm đối tượng và nếu bạn chỉ muốn liệt kê đến một điểm nhất định trong điều tra viên (ví dụ cho đến khi tìm thấy một đối tượng cụ thể, hoặc điều kiện được đáp ứng) và sử dụng cùng một điều tra viên sau khi thoát ra của vòng lặp, thường sẽ xảy ra trường hợp một số đối tượng sẽ bị bỏ qua.

Nếu bạn đang mã hóa cho OS X 10.6 / iOS 4.0 trở lên, bạn cũng có tùy chọn sử dụng API dựa trên khối để liệt kê các mảng và các bộ sưu tập khác:

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

Bạn cũng có thể sử dụng -enumerateObjectsWithOptions:usingBlock:và vượt qua NSEnumerationConcurrentvà / hoặc NSEnumerationReverselàm đối số tùy chọn.


10,4 hoặc sớm hơn

Thành ngữ tiêu chuẩn cho trước 10.5 là sử dụng NSEnumeratorvòng lặp and và while, như vậy:

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

Tôi khuyên bạn nên giữ nó đơn giản. Tự buộc mình vào một kiểu mảng là không linh hoạt, và việc tăng tốc độ sử dụng có mục đích-objectAtIndex: có ý nghĩa không đáng kể đối với sự cải thiện với phép liệt kê nhanh trên 10,5+. .

Khi sử dụng -objectEnumerator, bạn rất dễ dàng thay đổi sang một bộ sưu tập vô số khác (như an NSSet, các khóa trong một NSDictionary, v.v.) hoặc thậm chí chuyển sang -reverseObjectEnumeratorliệt kê một mảng ngược, tất cả không có thay đổi mã nào khác. Nếu mã lặp trong một phương thức, bạn thậm chí có thể chuyển vào bất kỳ NSEnumeratorvà mã nào thậm chí không phải quan tâm đến những gì nó lặp đi lặp lại. Hơn nữa, mộtNSEnumerator (ít nhất là những thứ được cung cấp bởi mã Apple) vẫn giữ bộ sưu tập mà nó liệt kê miễn là có nhiều đối tượng hơn, vì vậy bạn không phải lo lắng về việc một đối tượng tự động tồn tại sẽ tồn tại bao lâu.

Có lẽ điều lớn nhất mà một NSEnumerator(hoặc liệt kê nhanh) bảo vệ bạn là có một bộ sưu tập có thể thay đổi (mảng hoặc cách khác) thay đổi bên dưới bạn bạn không biết trong khi liệt kê nó. Nếu bạn truy cập các đối tượng theo chỉ mục, bạn có thể gặp phải các trường hợp ngoại lệ lạ hoặc lỗi do lỗi (thường là rất lâu sau khi sự cố xảy ra) có thể gây khó chịu cho việc gỡ lỗi. Việc liệt kê sử dụng một trong các thành ngữ tiêu chuẩn có hành vi "không nhanh", do đó, sự cố (gây ra bởi mã không chính xác) sẽ tự biểu hiện ngay lập tức khi bạn cố gắng truy cập vào đối tượng tiếp theo sau khi đột biến xảy ra. Khi các chương trình trở nên phức tạp và đa luồng hơn, hoặc thậm chí phụ thuộc vào thứ gì đó mà mã của bên thứ ba có thể sửa đổi, mã liệt kê dễ vỡ trở nên ngày càng có vấn đề. FTW đóng gói và trừu tượng! :-)



28
Lưu ý: hầu hết các trình biên dịch sẽ đưa ra cảnh báo về "while (object = [e nextObject])". Trong trường hợp này, bạn thực sự có nghĩa là sử dụng = thay vì ==. Để loại bỏ các cảnh báo, bạn có thể thêm dấu ngoặc đơn: "while ((object = [e nextObject]))".
Adam Rosenfield

Một điều cần nhớ với NSEnumerator là nó sẽ sử dụng nhiều bộ nhớ hơn (tạo một bản sao của mảng) so với cách tiếp cận objectWithIndex.
deatherikh

@QuinnTaylor Sử dụng for (id object in array), có phải là một cách để xác định chỉ mục hiện tại của các đối tượng trong mảng hay không cần phải bao gồm một bộ đếm riêng?
Coderama

1
Bạn sẽ cần một bộ đếm riêng, nhưng tôi khuyên bạn nên xem xét liệt kê dựa trên khối (bao gồm chỉ mục) nếu nó có sẵn cho bạn.
Quinn Taylor

Tôi kỳ lạ, nhưng tôi sử dụng một forvòng lặp như thế này:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
nielsbot

125

Đối với OS X 10.4.x trở về trước:

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

Dành cho OS X 10.5.x (hoặc iPhone) trở lên:

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

2
Trong ví dụ đầu tiên, bạn thậm chí không phải khai báo int bên ngoài vòng lặp. Điều này cũng hoạt động tốt và phạm vi biến độc đáo để bạn có thể sử dụng lại sau nếu cần: for (int i = 0; i <[myArray Count]; i ++) ... Nhưng cũng vậy, hãy lưu ý rằng việc gọi -count mỗi lần thông qua mảng có thể hủy bỏ lợi ích của việc sử dụng -objectAt Index:
Quinn Taylor

1
Và trong thực tế, nếu bạn đang liệt kê một bộ sưu tập có thể thay đổi, về mặt kỹ thuật sẽ chính xác hơn để kiểm tra số lượng mỗi lần qua vòng lặp. Tôi đã làm rõ câu trả lời của mình để giải thích rằng việc sử dụng NSEnumerator hoặc NSFastEnumutions có thể bảo vệ khỏi các đột biến đồng thời của mảng.
Quinn Taylor

Tương phản for (int i = 0; ...) là một phương ngữ ngôn ngữ C (C99 là tin tưởng), mà bản thân tôi sử dụng nhưng tôi không chắc đó là mặc định XCode.
deatherikh

C99 là mặc định thông qua Xcode 3.1.x - tại một số điểm trong tương lai, mặc định sẽ thay đổi thành GNU99, trong đó (trong số những thứ khác) hỗ trợ các hiệp hội và cấu trúc ẩn danh. Điều đó thật tuyệt ...
Quinn Taylor

2
Sử dụng for (NSUInteger i = 0, count = [myArray count]; i < count; i++)có lẽ là hiệu quả và ngắn gọn nhất mà bạn sẽ có được cho phương pháp này.
Quinn Taylor

17

Kết quả kiểm tra và mã nguồn ở bên dưới (bạn có thể đặt số lần lặp trong ứng dụng). Thời gian tính bằng mili giây và mỗi mục là kết quả trung bình của việc chạy thử nghiệm 5-10 lần. Tôi thấy rằng nhìn chung nó chính xác đến 2-3 chữ số có nghĩa và sau đó nó sẽ thay đổi theo mỗi lần chạy. Điều đó mang lại biên độ sai số dưới 1%. Thử nghiệm đã chạy trên iPhone 3G vì đó là nền tảng mục tiêu mà tôi quan tâm.

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

Các lớp được cung cấp bởi Cốc Cốc để xử lý các bộ dữ liệu (NSDipedia, NSArray, NSSet, v.v.) cung cấp một giao diện rất đẹp để quản lý thông tin, mà không phải lo lắng về sự quan liêu của quản lý bộ nhớ, phân bổ lại, v.v ... Tất nhiên điều này phải trả giá . Tôi nghĩ khá rõ ràng rằng việc sử dụng NSArray của NSNumbers sẽ chậm hơn một mảng C nổi cho các lần lặp đơn giản, vì vậy tôi đã quyết định thực hiện một số thử nghiệm và kết quả khá sốc! Tôi đã không mong đợi nó sẽ tệ đến thế. Lưu ý: các thử nghiệm này được tiến hành trên iPhone 3G vì đó là nền tảng mục tiêu tôi quan tâm.

Trong thử nghiệm này, tôi thực hiện một so sánh hiệu suất truy cập ngẫu nhiên rất đơn giản giữa một float C và NSArray của NSNumbers

Tôi tạo một vòng lặp đơn giản để tổng hợp nội dung của từng mảng và thời gian sử dụng chúng bằng mach_absolute_time (). NSMutableArray mất trung bình 400 lần nữa !! (không phải 400 phần trăm, chỉ dài hơn 400 lần! đó là 40.000% lâu hơn!).

Tiêu đề:

// Array_Speed_TestViewControll.h

// Kiểm tra tốc độ mảng

// Được tạo bởi Mehmet Akten vào ngày 05/02/2009.

// Bản quyền MSA Visuals Ltd. 2009. Bảo lưu mọi quyền.

#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

Thực hiện:

// Array_Speed_TestViewControll.m

// Kiểm tra tốc độ mảng

// Được tạo bởi Mehmet Akten vào ngày 05/02/2009.

// Bản quyền MSA Visuals Ltd. 2009. Bảo lưu mọi quyền.

    #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

Từ: memo.tv

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

Có sẵn kể từ khi giới thiệu các khối, điều này cho phép lặp lại một mảng với các khối. Cú pháp của nó không đẹp như liệt kê nhanh, nhưng có một tính năng rất thú vị: liệt kê đồng thời. Nếu thứ tự liệt kê không quan trọng và các công việc có thể được thực hiện song song mà không bị khóa, điều này có thể cung cấp một sự tăng tốc đáng kể trên hệ thống đa lõi. Thêm về điều đó trong phần liệt kê đồng thời.

[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

Ý tưởng đằng sau việc liệt kê nhanh là sử dụng truy cập mảng C nhanh để tối ưu hóa việc lặp lại. Không chỉ được cho là nhanh hơn NSEnumerator truyền thống, mà Objective-C 2.0 còn cung cấp một cú pháp rất súc tích.

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

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

NSEnumerator

Đây là một hình thức lặp bên ngoài: [myArray objectEnumerator] trả về một đối tượng. Đối tượng này có một phương thức nextObject mà chúng ta có thể gọi trong một vòng lặp cho đến khi nó trả về nil

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

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

objectAt Index: liệt kê

Sử dụng vòng lặp for làm tăng số nguyên và truy vấn đối tượng bằng cách sử dụng [myArray objectAtIndex: index] là dạng liệt kê cơ bản nhất.

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

////////////// Từ: darkdust.net


12

Ba cách là:

        //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. Là cách nhanh nhất để thực hiện và 3. với tự động hoàn thành hãy quên việc viết phong bì lặp.

7

Thêm eachphương thức trong của bạn NSArray category, bạn sẽ cần nó rất nhiều

Mã được lấy từ ObjectiveSugar

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

5

Đây là cách bạn khai báo một chuỗi các chuỗi và lặp qua chúng:

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

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

0

Dành cho Swift

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

Làm cái này :-

for (id object in array) 
{
        // statement
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.