как мне перебрать nsarray?

<p>я ищу стандартную идиому для перебора nsarray. мой код должен соответствовать os x 10.4+.</p>

<p>обычно предпочтительный код для 10.5 +/ios.</p><pre><code>for (id object in array) { // do something with object } </code></pre><p>эта конструкция используется для перечисления объектов в коллекции, которая соответствует протоколу <a href="https://developer.apple.com/documentation/foundation/nsfastenumeration"><code>nsfastenumeration</code></a> . этот подход имеет преимущество в скорости, поскольку он хранит указатели на несколько объектов (полученных с помощью одного вызова метода) в буфере и выполняет итерацию по ним, продвигаясь по буферу с использованием арифметики указателей. это <em>намного</em> быстрее, чем каждый раз вызывать <code>-objectatindex:</code> в цикле.</p><p>стоит также отметить, что хотя вы технически <em>можете</em> используете цикл for-in для перехода к <code>nsenumerator</code>, я обнаружил, что это сводит на нет практически все преимущества скорости быстрого перечисления. причина в том, что стандартная реализация <code>nsenumerator</code> <code>-countbyenumeratingwithstate:objects:count:</code> помещает только один объект в буфер при каждом вызове.</p><p>я сообщил об этом в <code>radar://6296108</code> (быстрое перечисление nsenumerators вяло), но оно было возвращено как не подлежащее исправлению. причина в том, что быстрое перечисление предварительно выбирает группу объектов, и если вы хотите перечислить только определенную точку в перечислителе (например, до тех пор, пока конкретный объект не будет найден, или условие не будет выполнено) и использовать тот же перечислитель после разрыва цикла, часто бывает, что несколько объектов будут пропущены.</p><p>если вы программируете для os x 10.6/ios 4.0 и выше, у вас также есть возможность использовать блочные api для перечисления массивов и других коллекций:</p><pre><code>[array enumerateobjectsusingblock:^(id object, nsuinteger idx, bool *stop) { // do something with object }]; </code></pre><p>вы также можете использовать <code>-enumerateobjectswithoptions:usingblock:</code> и передать <code>nsenumerationconcurrent</code> и/или <code>nsenumerationreverse</code> в качестве аргумента параметров.</p><hr><h3>10.4 или ранее</h3><p>стандартная идиома для pre-10.5 - использовать <code>nsenumerator</code> и цикл while, например, так:</p><pre><code>nsenumerator *e = [array objectenumerator]; id object; while (object = [e nextobject]) { // do something with object } </code></pre><p>я рекомендую держать это простым. привязка к типу массива негибка, и предполагаемое увеличение скорости использования <code>-objectatindex:</code> в любом случае незначительно для улучшения с быстрым перечислением на 10.5+. (быстрое перечисление фактически использует арифметику указателей на базовой структуре данных и устраняет большую часть накладных расходов при вызове метода.) преждевременная оптимизация никогда не бывает хорошей идеей - она приводит к более сложному коду для решения проблемы, которая в любом случае не является вашим узким местом.</p><p>используя <strong><code>-objectenumerator</code></strong>, вы очень легко переключаетесь на другую перечисляемую коллекцию (например, <code>nsset</code>, ключи в <code>nsdictionary</code> и т.д.) или даже переключаетесь на <strong><code>-reverseobjectenumerator</code></strong> для перечисления массива в обратном порядке, и все без другого кода изменения. если код итерации находится в методе, вы можете даже передать любую переменную <code>nsenumerator</code>, и коду не нужно даже заботиться о <em>что</em> при ее итерации. кроме того, <code>nsenumerator</code> (по крайней мере, предоставленный кодом apple) сохраняет коллекцию, которую она перечисляет, до тех пор, пока существует больше объектов, поэтому вам не нужно беспокоиться о том, как долго будет существовать автоматически выпущенный объект.</p><p>возможно, самая большая вещь, от которой <code>nsenumerator</code> (или быстрое перечисление) защищает вас, - это изменение изменяемой коллекции (массива или иным образом) под вами <em>без вашего ведома</em> во время перечисления. если вы обращаетесь к объектам по индексу, вы можете столкнуться со странными исключениями или ошибками (часто после того, как проблема возникла), которые могут быть ужасны для отладки. перечисление с использованием одной из стандартных идиом имеет поведение «fast-fast», поэтому проблема (вызванная неверным кодом) проявится сразу же, когда вы попытаетесь получить доступ к следующему объекту после того, как произошла мутация. поскольку программы становятся более сложными и многопоточными или даже зависят от того, что может изменить сторонний код, хрупкий код перечисления становится все более проблематичным. инкапсуляция и абстракция ftw! :-)</p><hr>

<p>для os x 10.4.x и предыдущих версий:</p><pre><code> int i; for (i = 0; i < [myarray count]; i++) { id myarrayelement = [myarray objectatindex:i]; ...do something useful with myarrayelement } </code></pre><p>для os x 10.5.x (или iphone) и выше:</p><pre><code>for (id myarrayelement in myarray) { ...do something useful with myarrayelement } </code></pre>

<p>результаты теста и исходный код приведены ниже (вы можете установить количество итераций в приложении). время указывается в миллисекундах, и каждая запись является средним результатом выполнения теста 5-10 раз. я обнаружил, что, как правило, с точностью до 2-3 значащих цифр, и после этого он будет меняться с каждым прогоном. это дает погрешность менее 1%. тест проводился на iphone 3g, так как это целевая платформа, которая меня интересовала.</p><pre><code>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 </code></pre><p>классы, предоставляемые cocoa для обработки наборов данных (nsdictionary, nsarray, nsset и т.д.), предоставляют очень удобный интерфейс для управления информацией, не беспокоясь о бюрократии управления памятью, перераспределения и т.д. конечно, это обходится дорого, хотя , я думаю, что совершенно очевидно, что использование nsarray для nsnumbers будет медленнее, чем для массива с плавающей точкой c для простых итераций, поэтому я решил провести несколько тестов, и результаты были довольно шокирующими! я не ожидал, что это будет так плохо. примечание: эти тесты проводятся на iphone 3g, так как это целевая платформа, которая меня заинтересовала.</p><p>в этом тесте я делаю очень простое сравнение производительности произвольного доступа между c float * и nsarray из nsnumbers</p><p>я создаю простой цикл для суммирования содержимого каждого массива и времени их с помощью mach\_absolute\_time (). nsmutablearray занимает в среднем в 400 раз больше времени !! (не на 400 процентов, а в 400 раз дольше! это на 40000% дольше!).</p><p>заголовок:</p><p>// array\_speed\_testviewcontroller.h</p><p>// тест скорости массива</p><p>// создано мехмет актен 05/02/2009.</p><p>// copyright msa visuals ltd. 2009. все права защищены.</p><pre><code>#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 </code></pre><p>реализация:</p><p>// array\_speed\_testviewcontroller.m</p><p>// тест скорости массива</p><p>// создано мехмет актен 05/02/2009.</p><p>// copyright msa visuals ltd. 2009. все права защищены.</p><pre><code> #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 </code></pre><p>от: memo.tv</p><p>////////////////////</p><p>доступно с момента появления блоков, это позволяет перебирать массив с блоками. его синтаксис не так хорош, как быстрое перечисление, но есть одна очень интересная особенность: одновременное перечисление. если порядок перечисления не важен и задания могут выполняться параллельно без блокировки, это может обеспечить значительное ускорение в многоядерной системе. подробнее об этом в разделе одновременного перечисления.</p><pre><code>[myarray enumerateobjectsusingblock:^(id object, nsuinteger index, bool *stop) { [self dosomethingwith:object]; }]; [myarray enumerateobjectswithoptions:nsenumerationconcurrent usingblock:^(id obj, nsuinteger idx, bool *stop) { [self dosomethingwith:object]; }]; </code></pre><p>/////////// nsfastenumerator</p><p>идея быстрого перечисления заключается в использовании быстрого доступа к массиву c для оптимизации итерации. мало того, что он должен быть быстрее, чем традиционный nsenumerator, но objective-c 2.0 также обеспечивает очень краткий синтаксис.</p><pre><code>id object; for (object in myarray) { [self dosomethingwith:object]; } </code></pre><p>/////////////////</p><p>nsenumerator</p><p>это форма внешней итерации: [myarray objectenumerator] возвращает объект. этот объект имеет метод nextobject, который мы можем вызывать в цикле, пока он не вернет ноль</p><pre><code>nsenumerator *enumerator = [myarray objectenumerator]; id object; while (object = [enumerator nextobject]) { [self dosomethingwith:object]; } </code></pre><p>/////////////////</p><p>objectatindex: перечисление</p><p>использование цикла for, который увеличивает целое число, и запрос объекта с помощью [myarray objectatindex: index] является наиболее простой формой перечисления.</p><pre><code>nsuinteger count = [myarray count]; for (nsuinteger index = 0; index < count ; index++) { [self dosomethingwith:[myarray objectatindex:index]]; } </code></pre><p>////////////// from: darkdust.net</p>

<p>три способа: </p><pre><code> //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 }]; </code></pre><ol start="2"> <li>это самый быстрый способ выполнения, и 3. с автозаполнением забудьте о написании конверта итерации.</li> </ol>

<p>добавьте метод <code>each</code> в свой <code>nsarray category</code>, он вам очень понадобится</p><p>код взят из <a href="https://github.com/supermarin/objectivesugar/blob/master/classes/nsarray%2bobjectivesugar.m">objectivesugar</a> </p><pre><code>- (void)each:(void (^)(id object))block { [self enumerateobjectsusingblock:^(id obj, nsuinteger idx, bool *stop) { block(obj); }]; } </code></pre>

<p>вот как вы объявляете массив строк и перебираете их:</p><pre><code>nsarray *langs = @[@"es", @"en", @"pt", @"it", @"fr"]; for (int i = 0; i < [langs count]; i++) { nsstring *lang = (nsstring*) [langs objectatindex:i]; nslog(@"%@, ",lang); } </code></pre>

<p>для свифта </p><pre><code>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 } </code></pre>