Jak mam to zrobić nad NSArray?
Szukam standardowego idiomu do iteracji nad NSArray. Mój kod musi być odpowiedni dla OS X 10.4+.
7 answers
Ogólnie preferowany kod dla 10.5+ / iOS.
for (id object in array) {
// do something with object
}
Ten konstrukt służy do wyliczania obiektów w zbiorze, który odpowiada NSFastEnumeration
protokół. To podejście ma przewagę szybkości, ponieważ przechowuje wskaźniki do kilku obiektów (uzyskiwanych za pomocą jednego wywołania metody) w buforze i iteruje przez nie, przechodząc przez bufor za pomocą arytmetyki wskaźników. To jest dużo szybciej niż wywołanie -objectAtIndex:
za każdym razem przez pętlę.
To także warto zauważyć, że chociaż technicznie można użyć pętli for-in, aby przejść przez NSEnumerator
, odkryłem, że to unieważnia praktycznie całą przewagę prędkości szybkiego wyliczania. Powodem jest to, że domyślna implementacja NSEnumerator
-countByEnumeratingWithState:objects:count:
umieszcza tylko jeden obiekt w buforze przy każdym wywołaniu.
Zgłosiłem to w radar://6296108
(Szybkie wyliczanie Nsenumeratorów jest powolne), ale zostało zwrócone jako nie do naprawy. Powodem jest to, że szybkie wyliczenie wstępnie pobiera grupę obiektów, a jeśli chcesz wyliczyć tylko do określonego punktu w wyliczeniu (np. do momentu znalezienia konkretnego obiektu lub spełnienia warunku) i użyć tego samego wyliczenia po wyrwaniu się z pętli, często zdarza się, że kilka obiektów zostanie pominiętych.
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
Możesz również użyć -enumerateObjectsWithOptions:usingBlock:
i przekazać NSEnumerationConcurrent
i / lub NSEnumerationReverse
jako argument options.
10.4 lub wcześniej
Standardowym idiomem dla pre-10.5 jest użycieNSEnumerator
i pętli while, W ten sposób:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
Zalecam utrzymanie tego w prostocie. Przywiązanie się do typu tablicy jest nieelastyczne, a rzekomy wzrost prędkości używania -objectAtIndex:
jest nieistotny dla poprawy z szybkim wyliczaniem na 10.5+ w każdym razie. (Fast enumeration faktycznie używa arytmetyki wskaźników na podstawowej strukturze danych i usuwa większość narzutów wywołania metody.) Przedwczesna optymalizacja nigdy nie jest dobrym pomysłem - powoduje, że kod Messiera rozwiązuje problem, który i tak nie jest Twoim wąskim gardłem.
Podczas stosowania -objectEnumerator
, bardzo łatwo można zmienić na inny zbiór liczbowy (np. NSSet
, Klucze w NSDictionary
itp.), a nawet przełączyć się na -reverseObjectEnumerator
aby wyliczyć tablicę wstecz, wszystko bez innych zmian kodu. Jeśli kod iteracji jest w metodzie, możesz nawet przekazać dowolną NSEnumerator
, A kod nie musi się nawet przejmować Co jest iteracyjne. Co więcej, NSEnumerator
(przynajmniej te dostarczone przez kod Apple) zachowuje kolekcję, którą wylicza, tak długo, jak jest więcej obiektów, więc nie musisz się martwić o to, jak długo będzie istniał obiekt z autorelementem.
Być może największą rzeczą NSEnumerator
(lub szybkie wyliczenie) chroni Cię przed jest posiadanie mutowalnego zbioru (tablicy lub w inny sposób) zmieniającego się pod tobą bez Twojej wiedzy podczas wyliczania go. Jeśli uzyskasz dostęp do obiektów przez indeks, możesz napotkać dziwne wyjątki lub błędy off-by-one (często długo po wystąpieniu problemu), które mogą być przerażające do debugowania. Wyliczanie przy użyciu jednego ze standardowych idiomów ma zachowanie "Fail-fast" , więc problem (spowodowany nieprawidłowym kodem) pojawi się natychmiast, gdy spróbujesz uzyskać dostęp do następnego obiektu po wystąpieniu mutacji. Ponieważ programy stają się bardziej złożone i wielowątkowe, a nawet zależą od czegoś, co może modyfikować kod innych firm, kruchy kod wyliczania staje się coraz bardziej problematyczne. Enkapsulacja I abstrakcja FTW! :-)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-20 21:48:04
Dla OS X 10.4.x i poprzedni:
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
Dla OS X 10.5.x (lub iPhone) i dalej:
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-02 15:17:46
Wyniki testu i Kod źródłowy są poniżej (możesz ustawić liczbę iteracji w aplikacji). Czas jest w milisekundach, a każdy wpis jest średnim wynikiem uruchomienia testu 5-10 razy. Odkryłem, że ogólnie jest dokładny do 2-3 znaczących cyfr, a potem będzie się różnić z każdym uruchomieniem. Daje to margines błędu poniżej 1%. Test działał na iPhone 3G, ponieważ jest to platforma docelowa, którą byłem zainteresowany.
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
Klasy przewidziane przez Cocoa dla obsługa zestawów danych (NSDictionary, NSArray, NSSet itp.) zapewniają bardzo ładny interfejs do zarządzania informacjami, bez konieczności martwienia się o biurokrację zarządzania pamięcią, realokacji itp. Oczywiście jest to jednak kosztowne. Myślę, że to dość oczywiste, że powiedzmy używanie NSArray z NSNumbers będzie wolniejsze niż tablica C pływaków dla prostych iteracji, więc postanowiłem zrobić kilka testów, a wyniki były dość szokujące! Nie spodziewałem się, że będzie tak źle. Uwaga: te testy są prowadzone na iPhone 3G, ponieważ jest to platforma docelowa, którą byłem zainteresowany.
W tym teście robię bardzo proste porównanie wydajności dostępu losowego między C float* i NSArray NSNumbers
Tworzę prostą pętlę do podsumowania zawartości każdej tablicy i zmierzenia jej za pomocą mach_absolute_time (). Nsmutablearray trwa średnio 400 razy dłużej!! (Nie 400 procent, tylko 400 razy dłużej! to 40,000% dłużej!).
Nagłówek:
// Array_Speed_TestViewController.h
// Test Prędkości Tablicy
// Created by Mehmet Akten on 05/02/2009.
// Copyright MSA Visuals Ltd. 2009. Wszelkie prawa zastrzeżone.
#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
Realizacja:
/ / Array_Speed_TestViewController.m
// Test Prędkości Tablicy
// Created by Mehmet Akten on 05/02/2009.
// Copyright MSA Visuals Ltd. 2009. Wszelkie prawa zastrzeżone.
#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
Od : memo.tv
////////////////////
Dostępne od wprowadzenia bloków, pozwala to na iterację tablicy z blokami. Jego składnia nie jest tak miła jak szybkie wyliczanie, ale jest jedna bardzo interesująca cecha: równoczesne wyliczanie. Jeśli kolejność wyliczeń nie jest ważna, a zadania mogą być wykonywane równolegle bez blokowania, może to zapewnić znaczne przyspieszenie w systemie wielordzeniowym. Więcej o tym w podsumowaniu sekcja.
[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
Ideą szybkiego wyliczania jest użycie szybkiego dostępu do tablicy C w celu optymalizacji iteracji. Nie tylko ma być szybszy niż tradycyjny NSEnumerator, ale Objective-C 2.0 zapewnia również bardzo zwięzłą składnię.
id object;
for (object in myArray) {
[self doSomethingWith:object];
}
/////////////////
NSEnumerator
Jest to forma iteracji zewnętrznej: [myarray objectEnumerator] zwraca obiekt. Obiekt ten posiada metodę nextObject, którą możemy wywołać w pętli dopóki nie zwróci nil
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
ObjectAtIndex: enumeration
Używanie pętli for, która zwiększa liczbę całkowitą i odpytywanie obiektu za pomocą [myarray objectAtIndex: index] jest najbardziej podstawową formą wyliczenia.
NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
[self doSomethingWith:[myArray objectAtIndex:index]];
}
////////////// Od : darkdust.net
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-05-11 15:30:38
Trzy sposoby to:
//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
}];
- jest najszybszą drogą w wykonaniu, a 3. z autocompletion zapomnij o pisaniu koperty iteracyjnej.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-31 07:11:20
Dodaj each
metodę w swoim NSArray category
, będziesz jej bardzo potrzebował
Kod zaczerpnięty z obiektów
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-01 17:14:21
Oto jak deklarujesz tablicę łańcuchów i powtarzasz nad nimi:
NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];
for (int i = 0; i < [langs count]; i++) {
NSString *lang = (NSString*) [langs objectAtIndex:i];
NSLog(@"%@, ",lang);
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-20 16:54:58