ฉันกำลังมองหาสำนวนมาตรฐานเพื่อย้ำผ่าน NSArray รหัสของฉันต้องเหมาะสำหรับ OS X 10.4+
ฉันกำลังมองหาสำนวนมาตรฐานเพื่อย้ำผ่าน NSArray รหัสของฉันต้องเหมาะสำหรับ OS X 10.4+
คำตอบ:
รหัสที่ต้องการโดยทั่วไปสำหรับ 10.5 + / iOS
for (id object in array) {
// do something with object
}
โครงสร้างนี้ใช้เพื่อระบุวัตถุในคอลเลกชันซึ่งสอดคล้องกับNSFastEnumeration
โปรโตคอล วิธีการนี้มีความได้เปรียบด้านความเร็วเพราะมันเก็บพอยน์เตอร์ไปยังวัตถุหลาย ๆ อัน (ที่ได้จากการเรียกใช้เมธอดเดียว) ในบัฟเฟอร์และวนซ้ำผ่านพวกมันโดยเลื่อนผ่านบัฟเฟอร์โดยใช้เลขคณิตของตัวชี้ นี้เป็นมากได้เร็วขึ้นกว่าการเรียก-objectAtIndex:
แต่ละครั้งที่ผ่านห่วง
นอกจากนี้ยังเป็นที่น่าสังเกตว่าในขณะที่คุณได้ในทางเทคนิคสามารถใช้สำหรับในวงไปยังขั้นตอนผ่านNSEnumerator
ผมได้พบว่าเป็นโมฆะเกือบทั้งหมดของข้อได้เปรียบที่ความเร็วของการแจงนับอย่างรวดเร็วนี้ เหตุผลคือการNSEnumerator
ใช้งานเริ่มต้นของ-countByEnumeratingWithState:objects:count:
สถานที่เพียงหนึ่งวัตถุในบัฟเฟอร์ในการโทรแต่ละครั้ง
ฉันรายงานสิ่งนี้ในradar://6296108
(การแจงนับอย่างรวดเร็วของ NSEnumerators จะซบเซา) แต่มันกลับมาเป็นไม่ต้องแก้ไข เหตุผลก็คือการแจงนับอย่างรวดเร็วล่วงหน้าดึงกลุ่มของวัตถุและถ้าคุณต้องการที่จะระบุเพียงจุดที่กำหนดในการแจกแจง (เช่นจนกว่าจะพบวัตถุเฉพาะหรือเงื่อนไขตรง) และใช้ตัวนับเดียวกันหลังจากแตกออก ของวงมันมักจะเป็นกรณีที่วัตถุหลายอย่างจะถูกข้าม
หากคุณกำลังเข้ารหัสสำหรับ OS X 10.6 / iOS 4.0 ขึ้นไปคุณยังมีตัวเลือกในการใช้ API แบบบล็อกเพื่อระบุอาร์เรย์และคอลเลกชันอื่น ๆ :
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
นอกจากนี้คุณยังสามารถใช้-enumerateObjectsWithOptions:usingBlock:
และผ่านNSEnumerationConcurrent
และ / หรือNSEnumerationReverse
เป็นอาร์กิวเมนต์ตัวเลือก
สำนวนมาตรฐานสำหรับ pre-10.5 คือการใช้NSEnumerator
ห่วงและในขณะที่เช่น:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
ฉันแนะนำให้ทำให้มันง่าย การผูกตัวเองกับประเภทอาเรย์นั้นไม่ยืดหยุ่นและการเพิ่มความเร็วในการใช้งานโดย-objectAtIndex:
ไม่ได้ตั้งใจนั้นไม่สำคัญต่อการปรับปรุงด้วยการแจงนับอย่างรวดเร็วที่ 10.5+ ต่อไป (การแจงนับอย่างรวดเร็วใช้ตัวชี้เลขคณิตจริงในโครงสร้างข้อมูลพื้นฐานและลบค่าใช้จ่ายส่วนใหญ่ของการเรียกเมธอด) การเพิ่มประสิทธิภาพก่อนกำหนดไม่ใช่ความคิดที่ดี
เมื่อใช้-objectEnumerator
งานคุณจะเปลี่ยนเป็นคอลเล็กชั่นนับจำนวนอื่นได้อย่างง่ายดาย (เช่นNSSet
กุญแจในNSDictionary
และอื่น ๆ ) หรือแม้กระทั่งเปลี่ยนไปใช้-reverseObjectEnumerator
เพื่อระบุอาเรย์ด้านหลังทั้งหมดโดยไม่มีการเปลี่ยนแปลงรหัสอื่น ๆ หากรหัสการวนซ้ำอยู่ในวิธีการคุณสามารถส่งผ่านรหัสใด ๆ ได้NSEnumerator
และรหัสนั้นไม่จำเป็นต้องสนใจว่าจะวนซ้ำก็ตาม นอกจากนี้NSEnumerator
(อย่างน้อยที่มีให้โดยรหัส Apple) จะยังคงมีการเก็บสะสมตราบใดที่มีวัตถุมากขึ้นดังนั้นคุณไม่ต้องกังวลเกี่ยวกับระยะเวลาที่วัตถุ autoreleased จะมีอยู่
บางทีสิ่งที่ใหญ่ที่สุดที่NSEnumerator
(หรือการแจงนับอย่างรวดเร็ว) ปกป้องคุณจากการที่มีคอลเลกชันที่ไม่แน่นอน (อาเรย์หรืออย่างอื่น) การเปลี่ยนแปลงภายใต้คุณโดยที่คุณไม่รู้ในขณะที่คุณแจกแจง หากคุณเข้าถึงวัตถุตามดัชนีคุณสามารถพบข้อยกเว้นแปลก ๆ หรือข้อผิดพลาดแบบ off-one (บ่อยครั้งหลังจากปัญหาเกิดขึ้น) ซึ่งอาจเป็นข้อผิดพลาดที่น่ากลัว การแจงนับโดยใช้หนึ่งในสำนวนมาตรฐานมีพฤติกรรม "ล้มเหลวอย่างรวดเร็ว" ดังนั้นปัญหา (เกิดจากรหัสที่ไม่ถูกต้อง) จะปรากฏขึ้นทันทีเมื่อคุณพยายามเข้าถึงวัตถุถัดไปหลังจากการกลายพันธุ์เกิดขึ้น เมื่อโปรแกรมมีความซับซ้อนมากขึ้นและมีหลายเธรดหรือแม้กระทั่งขึ้นอยู่กับสิ่งที่รหัสของบุคคลที่สามอาจแก้ไขรหัสการแจกแจงที่เปราะบางกลายเป็นปัญหามากขึ้น การห่อหุ้มและสิ่งที่เป็นนามธรรม FTW! :-)
for (id object in array)
เป็นวิธีการกำหนดวัตถุดัชนีปัจจุบันในอาร์เรย์หรือไม่หรือเคาน์เตอร์แยกต่างหากจะต้องรวม?
for
วงแบบนี้:for(;;) { id object = [ e nextObject ] ; if ( !e ) { break ; } ... your loop operation ... }
สำหรับ OS X 10.4.x และก่อนหน้า:
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
สำหรับ OS X 10.5.x (หรือ iPhone) และอื่น ๆ :
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
for (NSUInteger i = 0, count = [myArray count]; i < count; i++)
อาจเป็นวิธีที่มีประสิทธิภาพและรัดกุมที่สุดสำหรับวิธีการนี้
ผลลัพธ์ของการทดสอบและซอร์สโค้ดอยู่ด้านล่าง (คุณสามารถกำหนดจำนวนการวนซ้ำในแอป) เวลาเป็นมิลลิวินาทีและแต่ละรายการเป็นผลลัพธ์โดยเฉลี่ยของการทดสอบ 5-10 ครั้ง ฉันพบว่าโดยทั่วไปแล้วมันมีความถูกต้องถึงตัวเลขสองหลักและหลังจากนั้นมันก็จะแตกต่างกันไปในการวิ่งแต่ละครั้ง ที่ให้ขอบของข้อผิดพลาดน้อยกว่า 1% การทดสอบทำงานบน iPhone 3G เนื่องจากเป็นแพลตฟอร์มเป้าหมายที่ฉันสนใจ
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 สำหรับการจัดการชุดข้อมูล (NSDictionary, NSArray, NSSet ฯลฯ ) ให้อินเทอร์เฟซที่ดีมากสำหรับการจัดการข้อมูลโดยไม่ต้องกังวลเกี่ยวกับระบบราชการของการจัดการหน่วยความจำการจัดสรรใหม่ ฯลฯ แน่นอนว่า . ฉันคิดว่ามันค่อนข้างชัดเจนว่าการใช้ NSArray ของ NSNumbers จะช้ากว่า C Array of floats สำหรับการทำซ้ำอย่างง่ายดังนั้นฉันจึงตัดสินใจทำการทดสอบและผลลัพธ์ก็น่าตกใจมาก! ฉันไม่ได้คาดหวังว่ามันจะแย่ขนาดนี้ หมายเหตุ: การทดสอบเหล่านี้ดำเนินการบน iPhone 3G ซึ่งเป็นแพลตฟอร์มเป้าหมายที่ฉันสนใจ
ในการทดสอบนี้ฉันทำการเปรียบเทียบประสิทธิภาพการเข้าถึงแบบสุ่มที่ง่ายมากระหว่าง C float * และ NSArray ของ NSNumbers
ฉันสร้างลูปง่าย ๆ เพื่อสรุปเนื้อหาของแต่ละอาร์เรย์และใช้เวลาในการใช้ Mach_absolute_time () NSMutableArray ใช้เวลานานกว่า 400 เท่าโดยเฉลี่ย !! (ไม่ใช่ 400 เปอร์เซ็นต์นานกว่า 400 เท่า! นั่นนานกว่า 40,000%!)
หัวข้อ:
// Array_Speed_TestViewController.h
// การทดสอบความเร็วอาร์เรย์
// สร้างโดย Mehmet Akten เมื่อวันที่ 05/02/2552
// ลิขสิทธิ์ MSA Visuals Ltd. 2009 สงวนลิขสิทธิ์
#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
การดำเนินงาน:
// Array_Speed_TestViewController.m
// การทดสอบความเร็วอาร์เรย์
// สร้างโดย Mehmet Akten เมื่อวันที่ 05/02/2552
// ลิขสิทธิ์ MSA Visuals Ltd. 2009 สงวนลิขสิทธิ์
#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
จาก: memo.tv
////////////////////
มีให้ตั้งแต่เปิดตัวบล็อกสิ่งนี้จะอนุญาตให้วนซ้ำอาร์เรย์กับบล็อก ไวยากรณ์ของมันไม่ดีเท่าการแจงนับอย่างรวดเร็ว แต่มีคุณลักษณะที่น่าสนใจอย่างหนึ่งนั่นคือการแจงนับพร้อมกัน ถ้าลำดับการแจงนับไม่สำคัญและสามารถทำงานแบบขนานโดยไม่ล็อคสิ่งนี้สามารถให้การเร่งความเร็วที่รวดเร็วในระบบมัลติคอร์ เพิ่มเติมเกี่ยวกับสิ่งนั้นในส่วนการแจงนับพร้อมกัน
[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
แนวคิดเบื้องหลังการแจงนับอย่างรวดเร็วคือใช้การเข้าถึงอาร์เรย์ C อย่างรวดเร็วเพื่อเพิ่มประสิทธิภาพการทำซ้ำ ไม่เพียง แต่ควรจะเร็วกว่า NSEnumerator แบบดั้งเดิม แต่ Objective-C 2.0 ยังมีไวยากรณ์ที่กระชับ
id object;
for (object in myArray) {
[self doSomethingWith:object];
}
/////////////////
NSEnumerator
นี่คือรูปแบบของการวนซ้ำภายนอก: [myArray objectEnumerator] ส่งคืนวัตถุ วัตถุนี้มีวิธีถัดไปอ็อบเจ็กที่เราสามารถเรียกใช้ในลูปจนกว่ามันจะส่งกลับศูนย์
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
objectAtIndex: การแจงนับ
การใช้ห่วงสำหรับเพิ่มจำนวนเต็มและค้นหาวัตถุโดยใช้ [myArray objectAtIndex: index] เป็นรูปแบบพื้นฐานที่สุดของการแจงนับ
NSUInteger count = [myArray count];
for (NSUInteger index = 0; index < count ; index++) {
[self doSomethingWith:[myArray objectAtIndex:index]];
}
////////////// จาก: darkdust.net
สามวิธีคือ:
//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
}];
เพิ่มeach
วิธีการในของคุณNSArray category
คุณคุณจะต้องการมันมาก
รหัสที่นำมาจากObjectiveSugar
- (void)each:(void (^)(id object))block {
[self enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
block(obj);
}];
}
นี่คือวิธีที่คุณประกาศอาร์เรย์ของสตริงและวนซ้ำกับสตริงเหล่านั้น:
NSArray *langs = @[@"es", @"en", @"pt", @"it", @"fr"];
for (int i = 0; i < [langs count]; i++) {
NSString *lang = (NSString*) [langs objectAtIndex:i];
NSLog(@"%@, ",lang);
}
สำหรับสวิฟท์
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
}
ทำเช่นนี้ :-
for (id object in array)
{
// statement
}