Saya mencari idiom standar untuk beralih ke NSArray. Kode saya harus cocok untuk OS X 10.4+.
Saya mencari idiom standar untuk beralih ke NSArray. Kode saya harus cocok untuk OS X 10.4+.
Jawaban:
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 NSFastEnumeration
protokol. 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 NSEnumerator
implementasi 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 NSEnumerationConcurrent
dan / atau NSEnumerationReverse
sebagai argumen opsi.
Idi standar untuk pra-10.5 adalah dengan menggunakan NSEnumerator
loop 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 -reverseObjectEnumerator
untuk menghitung sebuah array mundur, semua tanpa perubahan kode lainnya. Jika kode iterasi adalah dalam metode, Anda bahkan bisa lulus dalam setiap NSEnumerator
dan 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! :-)
for (id object in array)
, apakah ada cara untuk juga menentukan objek indeks saat ini dalam array, atau apakah penghitung terpisah perlu dimasukkan?
for
loop seperti ini:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
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
}
for (NSUInteger i = 0, count = [myArray count]; i < count; i++)
mungkin yang paling efisien dan ringkas yang akan Anda dapatkan untuk pendekatan ini.
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
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
}];
Tambahkan each
metode 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);
}];
}
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
}
Melakukan hal ini :-
for (id object in array)
{
// statement
}