كيف يمكنني تكرار عبر NSArray?
-
13-09-2019 - |
سؤال
أنا أبحث عن مستوى لغة إلى تكرار عبر 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 هو بطيئة) ولكن تم إرجاعه لأنه لا يتم إصلاحه. والسبب هو أن التعداد السريع مسبقا مجموعة من الكائنات، وإذا كنت ترغب في التعداد فقط إلى نقطة معينة فقط في العداد (على سبيل المثال، يتم العثور على كائن معين، أو يتم استيفاء الشرط) واستخدم نفس العداد بعد الانهيار من الحلقة، سيكون هذا هو الحال في كثير من الأشياء التي سيتم تخطيها عدة أشياء.
إذا كنت ترميزا لنظام التشغيل X 10.6 / iOS 4.0 وما فوق، فستكون لديك أيضا خيار استخدام واجهات برمجة التطبيقية المستندة إلى Block لتعداد الصفائف والمجموعات الأخرى:
[array enumerateObjectsUsingBlock:^(id object, NSUInteger idx, BOOL *stop) {
// do something with object
}];
تستطيع ايضا استخذام -enumerateObjectsWithOptions:usingBlock:
وتمرير NSEnumerationConcurrent
و / أو NSEnumerationReverse
كما حجة الخيارات.
10.4 أو سابقا
المصطلح القياسي لما قبل 10.5 هو استخدام NSEnumerator
بينما الحلقة، مثل ذلك:
NSEnumerator *e = [array objectEnumerator];
id object;
while (object = [e nextObject]) {
// do something with object
}
أوصي الاحتفاظ بها بسيطة. ربط نفسك إلى نوع الصفيف غير مرن، وزيادة السرعة المزعومة في استخدام -objectAtIndex:
أمر ضئيل لتحسين التعداد السريع على 10.5+ على أي حال. (التعداد السريع يستخدم بالفعل حساب حساب مؤشر على هيكل البيانات الأساسي، ويزيل معظم الأسلوب استدعاء النفقات العامة.) الأمثل المبكر ليس فكرة جيدة - فهو ينتج عنه رمز Messier لحل مشكلة ليس عنق الزجاجة على أي حال.
عند استخدام -objectEnumerator
, ، يمكنك التغيير بسهولة جدا إلى مجموعة غير قابلة للتعدي (مثل NSSet
, ، مفاتيح في NSDictionary
, ، وما إلى ذلك)، أو حتى التبديل إلى -reverseObjectEnumerator
لتعداد صفيف للخلف، كل ذلك مع أي تغييرات رمز أخرى. إذا كان رمز التكرار في طريقة، فيمكنك نقله NSEnumerator
والرمز لا يضطر حتى يهتم ماذا او ما انها تكرار. علاوة على ذلك، NSEnumerator
(على الأقل أولئك الذين يوفرهم رمز Apple) يحتفظون بالتحصيل الذي يعداد طالما أن هناك المزيد من الأشياء، لذلك لا داعي للقلق بشأن المدة التي ستوفر فيها كائن التشغيل التلقائي.
ربما أكبر شيء NSEnumerator
(أو التعداد السريع) يحميك من أن وجود مجموعة قابلة للتغيير (صفيف أو غير ذلك) تتغير تحتها بدون معارفك بينما كنت تعطل ذلك. إذا كنت تصل إلى الكائنات حسب الفهرس، فيمكنك تشغيل استثناءات غريبة أو أخطاء خارجية (في كثير من الأحيان بعد فترة طويلة بعد حدوث المشكلة) التي يمكن أن تكون مروعة لتصحيح الأخطاء. يحتوي التعداد باستخدام إحدى التعابير القياسية على سلوك "فشل سريع"، لذلك سيظهر المشكلة (الناجمة عن التعليمات البرمجية غير الصحيحة) على الفور عند محاولة الوصول إلى الكائن التالي بعد حدوث طفرة. نظرا لأن البرامج تحصل على أكثر تعقيدا ومتعدد الخيوط، أو حتى تعتمد على شيء قد يعدل رمز الطرف الثالث، يصبح رمز التعداد الهش يصبح مشكلة بشكل متزايد. التغليف والتجريد FTW! :-)
نصائح أخرى
لنظام التشغيل × 10.4.x وال السابق:
int i;
for (i = 0; i < [myArray count]; i++) {
id myArrayElement = [myArray objectAtIndex:i];
...do something useful with myArrayElement
}
لنظام التشغيل × 10.5.x (أو iPhone) وما بعده:
for (id myArrayElement in myArray) {
...do something useful with myArrayElement
}
نتائج اختبار التعليمات البرمجية المصدر أدناه (يمكنك تعيين عدد من التكرارات في التطبيق).الوقت بالمللي ثانية و كل إدخال في المتوسط نتيجة تشغيل الاختبار 5-10 مرات.لقد وجدت هذا عموما هو دقيق 2-3 ارقام كبيرة و بعد ذلك سوف تختلف مع كل تشغيل.أن يعطي هامش خطأ أقل من 1%.الاختبار كان يعمل على الجيل الثالث 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
الفئات التي تقدمها الكاكاو على التعامل مع مجموعات البيانات (NSDictionary, NSArray ، NSSet.... الخ) توفر واجهة جميلة جدا لإدارة المعلومات دون الحاجة إلى القلق حول البيروقراطية في إدارة الذاكرة تخصيص الخ.بالطبع هذا لا يأتي بتكلفة بالرغم من ذلك.أعتقد أنه من الواضح جدا أن أقول باستخدام NSArray من NSNumbers سيكون أبطأ من ج مجموعة من يطفو على التكرار بسيطة, لذلك قررت أن تفعل بعض الفحوصات وكانت النتائج صادمة جدا!لم أكن أتوقع أن يكون بهذا السوء.ملاحظة:هذه الاختبارات التي أجريت على الجيل الثالث 3G اي فون كما أن الهدف منصة كنت مهتما في.
في هذا الاختبار أفعل بسيط جدا الوصول العشوائي مقارنة الأداء بين ج تطفو* و NSArray من NSNumbers
لقد خلق حلقة بسيطة لتلخيص محتويات كل مجموعة والوقت لهم باستخدام mach_absolute_time().على NSMutableArray يأخذ في المتوسط 400 مرات أطول!!(لا 400 في المئة فقط 400 مرات أطول!هذا 40,000% أطول!).
الرأس:
// Array_Speed_TestViewController.ح
// مجموعة اختبار سرعة
// تم إنشاؤها بواسطة محمد Akten على 05/02/2009.
// حقوق النشر MSA صور 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.م
// مجموعة اختبار سرعة
// تم إنشاؤها بواسطة محمد Akten على 05/02/2009.
// حقوق النشر MSA صور 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
////////////////////
متاح منذ إدخال كتل ، وهذا يسمح تكرار مجموعة مع كتل.بناء الجملة ليس لطيفا كما سريع التعداد ، ولكن هناك ميزة واحدة مثيرة جدا للاهتمام:المتزامنة التعداد.إذا كان تعداد أمر لا أهمية الوظائف التي يمكن أن يتم بالتوازي دون قفل, هذا يمكن أن توفر قدرا كبيرا من تسريع على multi-core النظام.المزيد حول ذلك في المتزامنة التعداد القسم.
[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 ، ولكن الهدف-C 2.0 يوفر أيضا موجزة جدا في بناء الجملة.
id object;
for (object in myArray) {
[self doSomethingWith:object];
}
/////////////////
NSEnumerator
هذا هو شكل من أشكال الخارجية التكرار:[myArray objectEnumerator] بإرجاع كائن.يحتوي هذا الكائن على طريقة nextObject نتمكن من الاتصال في حلقة حتى تعود النيل
NSEnumerator *enumerator = [myArray objectEnumerator];
id object;
while (object = [enumerator nextObject]) {
[self doSomethingWith:object];
}
/////////////////
objectAtIndex:التعداد
باستخدام حلقة for مما يزيد عدد صحيح و الاستعلام عن كائن باستخدام [myArray objectAtIndex:فهرس] هو أبسط شكل من التعداد.
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
}];
- هي أسرع طريقة في التنفيذ، و 3. مع تنسيق الإكمال التلقائي حول مغلف التكرار.
يضيف each
طريقة في الخاص بك NSArray category
, ، ستحتاج إليها كثيرا
رمز مأخوذ من الاعتراضات الأجواء
- (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
}