Bir NSArray üzerinden nasıl yineleyebilirim?


Yanıtlar:


667

10.5 + / iOS için genellikle tercih edilen kod.

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

Bu yapı, NSFastEnumerationprotokole uyan bir koleksiyondaki nesneleri numaralandırmak için kullanılır . Bu yaklaşımın bir hız avantajı vardır, çünkü bir tamponda birkaç nesneye (tek bir yöntem çağrısı yoluyla elde edilen) işaretçileri saklar ve işaretçi aritmetiği kullanılarak tamponda ilerleyerek bunlar üzerinden yineler. Bu, döngü boyunca her seferinde çağırmaktan çok daha hızlıdır -objectAtIndex:.

Bu teknik olarak ise dikkati çekiyor var olabilir kullanmak bir for-bir ile adıma döngü NSEnumeratorbuldum ki bu da tehlikeye sokan neredeyse tüm hızlı numaralandırma hız avantajı. Bunun nedeni, varsayılan NSEnumeratoruygulama -countByEnumeratingWithState:objects:count:yerleri her çağrıda tamponu içinde yalnızca bir nesne.

Bunu radar://6296108(NSEnumerators'ın hızlı numaralandırması durgun) olarak bildirdim, ancak Düzeltilmeyecek şekilde döndürüldü. Bunun nedeni, hızlı numaralandırmanın bir grup nesneyi önceden getirmesidir ve numaralandırıcıda yalnızca belirli bir noktaya numaralandırmak istiyorsanız (örneğin belirli bir nesne bulunana veya koşul karşılanana kadar) ve ayrıldıktan sonra aynı numaralandırıcıyı kullanın genellikle birkaç nesnenin atlanması söz konusu olabilir.

OS X 10.6 / iOS 4.0 ve üstü için kod yazıyorsanız, dizileri ve diğer koleksiyonları numaralandırmak için blok tabanlı API'leri kullanma seçeneğiniz de vardır:

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

Ayrıca seçenekler argümanı olarak -enumerateObjectsWithOptions:usingBlock:ve geçebilir NSEnumerationConcurrentve / veya kullanabilirsiniz NSEnumerationReverse.


10.4 veya öncesi

10.5 öncesi için standart deyim bir NSEnumeratorand while döngüsü kullanmaktır, şöyle:

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

Basit tutmanızı tavsiye ederim. Kendinizi bir dizi türüne bağlamak esnek değildir ve kullanımdaki hızlı artış, -objectAtIndex:yine de 10.5+ üzerinde hızlı numaralandırma ile iyileştirme açısından önemsizdir. (Hızlı numaralandırma aslında temeldeki veri yapısı üzerinde işaretçi aritmetiği kullanır ve yöntem çağrısı ek yükünü kaldırır.) Erken optimizasyon asla iyi bir fikir değildir - yine de darboğazınız olmayan bir sorunu çözmek için daha karmaşık bir kodla sonuçlanır.

Kullanırken -objectEnumerator, kolayca başka bir numaralandırılabilir koleksiyona (bir NSSet, bir anahtarlar NSDictionary, vb. Gibi) kolayca geçiş yapabilir veya hatta -reverseObjectEnumeratorbir diziyi geriye doğru numaralandırmak için geçiş yapabilirsiniz; Yineleme kodu bir yöntemdeyse, herhangi bir tanesini bile geçebilirsiniz NSEnumeratorve kodun yinelediği şeyi umursamaya bile gerek yoktur . Ayrıca, NSEnumerator(en azından Apple kodu tarafından sağlananlar), daha fazla nesne olduğu sürece numaralandırdığı koleksiyonu korur, bu nedenle otomatik olarak yayımlanan bir nesnenin ne kadar süreceği konusunda endişelenmenize gerek yoktur.

Belki de bir NSEnumerator(veya hızlı numaralandırma) sizi koruyan en büyük şey, numaralandırma sırasında sizin bilginiz olmadan altınızda değiştirilebilir bir koleksiyon (dizi veya başka türlü) bir değişiklik yapmaktır . Nesneleri dizine göre erişirseniz, hata ayıklamak için korkunç olabilecek garip istisnalar veya tek tek hatalar (genellikle sorun ortaya çıktıktan uzun süre sonra) ile karşılaşabilirsiniz. Standart deyimlerden birini kullanarak numaralandırma "başarısız-hızlı" davranışı vardır, bu nedenle mutasyon meydana geldikten sonra bir sonraki nesneye erişmeye çalıştığınızda sorun (yanlış koddan kaynaklanan) hemen kendini gösterir. Programlar daha karmaşık ve çok iş parçacıklı hale geldikçe veya üçüncü taraf kodunun değiştirebileceği bir şeye bağlı olduğundan, hassas numaralandırma kodu giderek daha sorunlu hale gelir. Kapsülleme ve soyutlama FTW! :-)



28
Not: Çoğu derleyici "while (object = [e nextObject])" hakkında bir uyarı verecektir. Bu durumda, aslında == yerine = kullanmak istediğiniz anlamına gelir. Uyarıları gizlemek için fazladan parantez ekleyebilirsiniz: "while ((object = [e nextObject]))" ".
Adam Rosenfield

NSEnumerator ile hatırlanması gereken bir diğer şey, objectWithIndex yaklaşımından daha fazla bellek (dizinin bir kopyasını oluşturur) kullanmasıdır.
loseerikh

@QuinnTaylor for (id object in array), dizideki nesnelerin geçerli dizinini de belirlemenin bir yolu mu yoksa ayrı bir sayacın dahil edilmesi gerekiyor mu?
Coderama

1
Ayrı bir sayaca ihtiyacınız olacak, ancak sizin için uygunsa blok tabanlı numaralandırmayı (dizini içerir) dikkate almanızı öneririm.
Quinn Taylor

Ben forfor(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
tuhafım

125

OS X 10.4.x ve öncesi için:

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

OS X 10.5.x (veya iPhone) ve üstü için:

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

2
İlk örnekte, int'i döngü dışında bildirmeniz bile gerekmez. Bu da aynı şekilde çalışır ve değişkeni güzelce kapsamaktadır, böylece gerekirse daha sonra tekrar kullanabilirsiniz: for (int i = 0; i <[myArray sayısı]; i ++) ... Ama aynı zamanda, dizi aracılığıyla -objectAtIndex kullanmanın yararını iptal edebilirsiniz:
Quinn Taylor

1
Ve gerçekte, değiştirilebilir bir koleksiyonu numaralandırıyorsanız, sayımı döngüden her seferinde kontrol etmek teknik olarak daha doğrudur. Cevabımı NSEnumerator veya NSFastEnumeration kullanarak dizinin eşzamanlı mutasyonlarından koruyabileceğini açıklamak için açıkladım.
Quinn Taylor

For (int i = 0; ...) contruct, kendim kullandığım bir C dili lehçesi (C99 inanıyorum), ama XCode varsayılan olduğundan emin değildim.
loseerikh

C99, Xcode 3.1.x aracılığıyla varsayılan değerdir - gelecekteki bir noktada varsayılan, (diğer şeylerin yanı sıra) anonim sendikaları ve yapıları destekleyen GNU99 olarak değişecektir. Bu güzel olmalı ...
Quinn Taylor

2
Kullanmak for (NSUInteger i = 0, count = [myArray count]; i < count; i++)muhtemelen bu yaklaşım için alacağınız en verimli ve özlüdür.
Quinn Taylor

17

Test ve kaynak kodun sonuçları aşağıdadır (uygulamada yineleme sayısını ayarlayabilirsiniz). Süre milisaniye cinsindendir ve her giriş, testi 5-10 kez çalıştırmanın ortalama sonucudur. Genellikle 2-3 anlamlı basamağa kadar doğru olduğunu gördüm ve bundan sonra her çalışmada değişecektir. Bu,% 1'den daha az bir hata payı verir. Test, ilgilendiğim hedef platform olduğu için bir iPhone 3G'de çalışıyordu.

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

Cocoa tarafından veri setlerini (NSDictionary, NSArray, NSSet vb.) İşlemek için sağlanan sınıflar, bellek yönetimi, yeniden tahsis vb. Bürokrasisi hakkında endişelenmek zorunda kalmadan, bilgileri yönetmek için çok güzel bir arayüz sağlar. . Ben NSNumbers bir NSArray kullanarak basit iterasyonlar için bir yüzer C Array daha yavaş olacağını söylemek çok açık olduğunu düşünüyorum, bu yüzden bazı testler yapmaya karar verdim ve sonuçlar oldukça şok edici oldu! Bu kadar kötü olmasını beklemiyordum. Not: Bu testler, ilgilendiğim hedef platform olduğu için bir iPhone 3G'de gerçekleştirilir.

Bu testte bir C float * ve NSNumbers NSArray arasında çok basit bir rasgele erişim performansı karşılaştırması yapıyorum

Ben her dizinin içeriğini özetlemek ve mach_absolute_time () kullanarak onları zamanlamak için basit bir döngü oluşturun. NSMutableArray ortalama 400 kat daha uzun sürer !! (yüzde 400 değil, sadece 400 kat daha uzun! bu% 40.000 daha uzun!).

Başlık:

// Array_Speed_TestViewController.h

// Dizi Hız Testi

// Mehmet Akten tarafından 05/02/2009 tarihinde hazırlandı.

// Telif Hakkı MSA Visuals Ltd. 2009. Tüm hakları saklıdır.

#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

Uygulama:

// Array_Speed_TestViewController.m

// Dizi Hız Testi

// Mehmet Akten tarafından 05/02/2009 tarihinde hazırlandı.

// Telif Hakkı MSA Visuals Ltd. 2009. Tüm hakları saklıdır.

    #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

Gönderen: memo.tv

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

Blokların tanıtılmasından bu yana kullanılabilir, bu bloklarla bir dizi yinelemeye izin verir. Sözdizimi hızlı numaralandırma kadar hoş değil, ama çok ilginç bir özellik var: eşzamanlı numaralandırma. Numaralandırma sırası önemli değilse ve işler kilitlenmeden paralel olarak yapılabilirse, bu çok çekirdekli bir sistemde önemli bir hızlanma sağlayabilir. Bununla ilgili daha fazla bilgi eşzamanlı numaralandırma bölümünde.

[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

Hızlı numaralandırmanın arkasındaki fikir, yinelemeyi optimize etmek için hızlı C dizisi erişimini kullanmaktır. Sadece geleneksel NSEnumerator'dan daha hızlı olması değil, aynı zamanda Objective-C 2.0 da çok özlü bir sözdizimi sağlar.

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

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

NSEnumerator

Bu bir dış yineleme şeklidir: [myArray objectEnumerator] bir nesne döndürür. Bu nesnenin, nil döndürülene kadar bir döngüde çağırabileceğimiz nextObject yöntemi var.

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

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

objectAtIndex: numaralandırma

Bir tamsayıyı artıran bir for döngüsü kullanmak ve [myArray objectAtIndex: index] kullanarak nesneyi sorgulamak en temel numaralandırma biçimidir.

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

////////////// Gönderen: darkdust.net


12

Üç yol:

        //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. Yürütmenin en hızlı yoludur ve 3. otomatik tamamlama ile yineleme zarfı yazmayı unutun.

7

eachYöntemini ekle NSArray category, çok ihtiyacın olacak

ObjectiveSugar'dan alınan kod

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

5

Bir dizi dizeyi nasıl bildireceğinizi ve bunları yinelediğinizi aşağıda görebilirsiniz:

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

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

0

Swift için

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

Bunu yap :-

for (id object in array) 
{
        // statement
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.