Bagaimana cara saya mengulang NSArray?

Jawaban:

667

Kode yang umumnya lebih disukai untuk 10,5 + / iOS.

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

Konstruk ini digunakan untuk menghitung objek dalam koleksi yang sesuai dengan NSFastEnumerationprotokol. Pendekatan ini memiliki keunggulan kecepatan karena menyimpan pointer ke beberapa objek (diperoleh melalui pemanggilan metode tunggal) dalam buffer dan beralih melalui mereka dengan memajukan melalui buffer menggunakan aritmatika pointer. Ini jauh lebih cepat daripada memanggil -objectAtIndex:setiap kali melalui loop.

Perlu juga dicatat bahwa sementara Anda secara teknis dapat menggunakan for-in loop untuk melangkah melalui NSEnumerator, saya telah menemukan bahwa ini membatalkan hampir semua keuntungan kecepatan enumerasi cepat. Alasannya adalah bahwa NSEnumeratorimplementasi default -countByEnumeratingWithState:objects:count:hanya menempatkan satu objek di buffer pada setiap panggilan.

Saya melaporkan ini dalam radar://6296108(Pencacahan Cepat NSEnumerator lamban) tetapi dikembalikan sebagai Tidak Akan Diperbaiki. Alasannya adalah bahwa penghitungan cepat pra-mengambil sekelompok objek, dan jika Anda ingin menghitung hanya ke titik tertentu dalam pencacah (misalnya sampai objek tertentu ditemukan, atau kondisi terpenuhi) dan menggunakan pencacah yang sama setelah keluar dari loop, akan sering terjadi bahwa beberapa objek akan dilewati.

Jika Anda mengkodekan untuk OS X 10.6 / iOS 4.0 dan di atasnya, Anda juga memiliki opsi untuk menggunakan API berbasis blok untuk menghitung array dan koleksi lainnya:

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

Anda juga dapat menggunakan -enumerateObjectsWithOptions:usingBlock:dan meneruskan NSEnumerationConcurrentdan / atau NSEnumerationReversesebagai argumen opsi.


10.4 atau lebih awal

Idi standar untuk pra-10.5 adalah dengan menggunakan NSEnumeratorloop sementara, seperti:

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

Saya sarankan menjaganya agar tetap sederhana. Mengikat diri sendiri ke jenis array tidak fleksibel, dan peningkatan kecepatan yang diklaim -objectAtIndex:tidak signifikan terhadap peningkatan dengan penghitungan cepat pada 10,5+. (Penghitungan cepat sebenarnya menggunakan pointer aritmatika pada struktur data yang mendasarinya, dan menghapus sebagian besar overhead metode panggilan.) Optimalisasi prematur tidak pernah merupakan ide yang baik - ini menghasilkan kode yang lebih berantakan untuk menyelesaikan masalah yang bukan merupakan hambatan Anda.

Saat menggunakan -objectEnumerator, Anda sangat mudah mengubah ke koleksi enumerable lain (seperti NSSet, kunci dalam NSDictionary, dll.), Atau bahkan beralih ke -reverseObjectEnumeratoruntuk menghitung sebuah array mundur, semua tanpa perubahan kode lainnya. Jika kode iterasi adalah dalam metode, Anda bahkan bisa lulus dalam setiap NSEnumeratordan kode bahkan tidak harus peduli tentang apa itu iterasi. Lebih lanjut, sebuah NSEnumerator(setidaknya yang disediakan oleh kode Apple) mempertahankan koleksi yang disebutkan selama ada lebih banyak objek, jadi Anda tidak perlu khawatir tentang berapa lama objek autoreleased akan ada.

Mungkin hal terbesar yang NSEnumerator(atau penghitungan cepat) melindungi Anda dari adalah memiliki koleksi yang dapat berubah (array atau lainnya) berubah di bawah Anda tanpa sepengetahuan Anda saat Anda menghitungnya. Jika Anda mengakses objek berdasarkan indeks, Anda bisa mengalami pengecualian aneh atau kesalahan satu per satu (seringkali lama setelah masalah terjadi) yang bisa mengerikan untuk debug. Pencacahan menggunakan salah satu idiom standar memiliki perilaku "gagal-cepat", sehingga masalah (disebabkan oleh kode yang salah) akan segera muncul ketika Anda mencoba mengakses objek berikutnya setelah mutasi terjadi. Ketika program menjadi lebih kompleks dan multi-utas, atau bahkan bergantung pada sesuatu yang dapat dimodifikasi oleh kode pihak ketiga, kode enumerasi yang rapuh menjadi semakin bermasalah. Enkapsulasi dan abstraksi FTW! :-)


Quinn Taylor
sumber
28
Catatan: kebanyakan kompiler akan memberi peringatan tentang "while (object = [e nextObject])". Dalam hal ini, Anda sebenarnya DO bermaksud menggunakan = bukannya ==. Untuk menekan peringatan, Anda dapat menambahkan tanda kurung tambahan: "while ((object = [e nextObject]))".
Adam Rosenfield
Satu hal yang perlu diingat dengan NSEnumerator adalah bahwa ia akan menggunakan lebih banyak memori (membuat salinan array) daripada pendekatan objectWithIndex.
diederikh
@ QuinnTaylor Menggunakan for (id object in array), apakah ada cara untuk juga menentukan objek indeks saat ini dalam array, atau apakah penghitung terpisah perlu dimasukkan?
Coderama
1
Anda memerlukan penghitung yang terpisah, tetapi saya sarankan mempertimbangkan enumerasi berbasis blok (yang termasuk indeks) jika tersedia untuk Anda.
Quinn Taylor
Saya aneh, tetapi saya menggunakan forloop seperti ini:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
nielsbot
125

Untuk OS X 10.4.x dan sebelumnya:

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

Untuk OS X 10.5.x (atau iPhone) dan lebih tinggi:

for (id myArrayElement in myArray) {
   ...do something useful with myArrayElement
}
diederikh
sumber
2
Pada contoh pertama, Anda bahkan tidak perlu mendeklarasikan int di luar loop. Ini berfungsi dengan baik, dan cakupan variabel dengan baik sehingga Anda dapat menggunakannya kembali nanti jika diperlukan: untuk (int i = 0; i <[jumlah myArray]; i ++) ... Tetapi juga, perlu diketahui bahwa memanggil -hitung setiap kali melalui array dapat membatalkan manfaat menggunakan -objectAtIndex:
Quinn Taylor
1
Dan pada kenyataannya, jika Anda menghitung koleksi yang bisa berubah, secara teknis lebih tepat untuk memeriksa jumlah setiap kali melalui loop. Saya mengklarifikasi jawaban saya untuk menjelaskan bahwa menggunakan NSEnumerator atau NSFastEnumeration dapat melindungi dari mutasi array secara bersamaan.
Quinn Taylor
Konstruksi for (int i = 0; ...) adalah dialek bahasa C (C99 percaya), yang saya sendiri gunakan tetapi saya tidak yakin itu adalah default XCode.
diederikh
C99 adalah standar naik melalui Xcode 3.1.x - di beberapa titik di masa depan standar akan berubah menjadi GNU99, yang (antara lain) mendukung serikat dan struct anonim. Itu seharusnya menyenangkan ...
Quinn Taylor
2
Menggunakan for (NSUInteger i = 0, count = [myArray count]; i < count; i++)mungkin yang paling efisien dan ringkas yang akan Anda dapatkan untuk pendekatan ini.
Quinn Taylor
17

Hasil tes dan kode sumber di bawah ini (Anda dapat mengatur jumlah iterasi dalam aplikasi). Waktunya dalam milidetik, dan setiap entri adalah hasil rata-rata menjalankan tes 5-10 kali. Saya menemukan bahwa umumnya akurat untuk 2-3 digit signifikan dan setelah itu akan bervariasi setiap kali dijalankan. Itu memberikan margin kesalahan kurang dari 1%. Tes berjalan pada iPhone 3G karena itulah platform target yang saya minati.

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

Kelas-kelas yang disediakan oleh Cocoa untuk menangani set data (NSDictionary, NSArray, NSSet dll.) Menyediakan antarmuka yang sangat bagus untuk mengelola informasi, tanpa harus khawatir tentang birokrasi manajemen memori, realokasi, dll. . Saya pikir itu cukup jelas bahwa mengatakan menggunakan NSArray dari NSNumber akan lebih lambat daripada C Array float untuk iterasi sederhana, jadi saya memutuskan untuk melakukan beberapa tes, dan hasilnya cukup mengejutkan! Saya tidak berharap ini akan seburuk ini. Catatan: tes ini dilakukan pada iPhone 3G karena itulah platform target yang saya minati.

Dalam tes ini saya melakukan perbandingan kinerja akses acak yang sangat sederhana antara C float * dan NSArray dari NSNumbers

Saya membuat loop sederhana untuk meringkas isi dari setiap array dan mengatur waktu menggunakan mach_absolute_time (). NSMutableArray memakan waktu rata-rata 400 kali lebih lama !! (bukan 400 persen, hanya 400 kali lebih lama! itu 40.000% lebih lama!).

Tajuk:

// Array_Speed_TestViewController.h

// Tes Kecepatan Array

// Dibuat oleh Mehmet Akten pada 05/02/2009.

// Hak cipta MSA Visuals Ltd. 2009. Hak cipta dilindungi undang-undang.

#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

Penerapan:

// Array_Speed_TestViewController.m

// Tes Kecepatan Array

// Dibuat oleh Mehmet Akten pada 05/02/2009.

// Hak cipta MSA Visuals Ltd. 2009. Hak cipta dilindungi undang-undang.

    #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

Dari: memo.tv

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

Tersedia sejak pengenalan blok, ini memungkinkan untuk mengulangi array dengan blok. Sintaksnya tidak sebagus pencacahan cepat, tetapi ada satu fitur yang sangat menarik: pencacahan serentak. Jika urutan enumerasi tidak penting dan pekerjaan dapat dilakukan secara paralel tanpa mengunci, ini dapat memberikan peningkatan yang cukup besar pada sistem multi-core. Lebih lanjut tentang itu di bagian enumerasi bersamaan.

[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

Gagasan di balik penghitungan cepat adalah menggunakan akses array C cepat untuk mengoptimalkan iterasi. Tidak hanya itu seharusnya lebih cepat daripada NSEnumerator tradisional, tetapi Objective-C 2.0 juga menyediakan sintaksis yang sangat ringkas.

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

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

NSEnumerator

Ini adalah bentuk iterasi eksternal: [myArray objectEnumerator] mengembalikan objek. Objek ini memiliki metode nextObject yang dapat kita panggil secara berulang sampai mengembalikan nihil

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

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

objectAtIndex: enumeration

Menggunakan for for yang meningkatkan bilangan bulat dan menanyakan objek menggunakan [myArray objectAtIndex: index] adalah bentuk paling dasar dari enumerasi.

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

////////////// Dari: darkdust.net

Hitendra Solanki
sumber
12

Tiga cara tersebut adalah:

        //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. Apakah cara tercepat dalam eksekusi, dan 3. dengan pelengkapan otomatis lupa tentang menulis amplop iterasi.
Javier Calatrava Llavería
sumber
7

Tambahkan eachmetode di Anda NSArray category, Anda akan membutuhkannya

Kode diambil dari ObjectiveSugar

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

Inilah cara Anda mendeklarasikan serangkaian string dan beralihinya:

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

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

Untuk 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
}
Nilesh R Patel
sumber
-1

Melakukan hal ini :-

for (id object in array) 
{
        // statement
}

sumber