какой лучший способ поместить c-структуру в nsarray?

<p>какой обычный способ хранить c-структуры в <code>nsarray</code>? преимущества, недостатки, обработка памяти?</p><p>примечательно, в чем разница между <code>valuewithbytes</code> и <code>valuewithpointer</code> - поднятые джастином и сомом ниже.</p><p> <a href="http://developer.apple.com/library/mac/#documentation/cocoa/conceptual/numbersandvalues/articles/values.html#//apple\_ref/doc/uid/20000174-bajjhdeg">вот ссылка</a> на обсуждение apple <code>valuewithbytes:objctype:</code> для будущих читателей ...</p><p>для некоторого бокового мышления и больше внимания к производительности, евгений поднял вопрос об использовании <code>stl::vector</code> в <strong>c++</strong>.</p><p><em>(в связи с этим возникает интересная проблема: существует ли быстрая библиотека c, похожая на <code>stl::vector</code>, но намного более легкая, что обеспечивает минимальную "аккуратную обработку массивов" ...?)</em></p><p>итак, оригинальный вопрос ...</p><p>например:</p><pre><code>typedef struct \_megapoint { float w,x,y,z; } megapoint; </code></pre><p>итак: каков нормальный, лучший идиоматический способ хранения собственной структуры, такой как эта, в <code>nsarray</code>, и как вы обращаетесь с памятью в этой идиоме?</p><p>обратите внимание, что я специально ищу обычную идиому для хранения структур. конечно, можно избежать этой проблемы, создав новый маленький класс. однако я хочу знать, как обычная идиома для фактического размещения структур в массиве, спасибо.</p><p>кстати, вот подход nsdata, который возможно? не самый лучший ...</p><pre><code>megapoint p; nsarray *a = [nsarray arraywithobjects: [nsdata datawithbytes:&p length:sizeof(megapoint)], [nsdata datawithbytes:&p length:sizeof(megapoint)], [nsdata datawithbytes:&p length:sizeof(megapoint)], nil]; </code></pre><p>кстати, в качестве ориентира и благодаря джаррету харди, вот как хранить <code>cgpoints</code> и аналогичные в <code>nsarray</code>:</p><pre><code>nsarray *points = [nsarray arraywithobjects: [nsvalue valuewithcgpoint:cgpointmake(6.9, 6.9)], [nsvalue valuewithcgpoint:cgpointmake(6.9, 6.9)], nil]; </code></pre><p>(см. <a href="https://stackoverflow.com/questions/899600/how-can-i-add-cgpoint-objects-to-an-nsarray-the-easy-way">как я могу легко добавить объекты cgpoint в nsarray?</a> )</p>

<p> <a href="http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/classes/nsvalue\_class/reference/reference.html">nsvalue</a> не только поддерживает структуры coregraphics - вы также можете использовать его для своих собственных. я бы порекомендовал это сделать, поскольку класс, вероятно, имеет меньший вес, чем <code>nsdata</code> для простых структур данных.</p><p>просто используйте выражение, подобное следующему:</p><pre><code>[nsvalue valuewithbytes:&p objctype:@encode(megapoint)]; </code></pre><p>и чтобы вернуть значение:</p><pre><code>megapoint p; [value getvalue:&p]; </code></pre>

<p>я бы посоветовал вам придерживаться маршрута <a href="http://developer.apple.com/library/ios/#documentation/cocoa/reference/foundation/classes/nsvalue\_class/reference/reference.html"><code>nsvalue</code></a> , но если вы действительно хотите сохранить типы данных 'ol <code>struct</code> в вашем nsarray (и других объектах коллекции в какао), вы можете сделать это, хотя и косвенно, используя core фундамент и <a href="http://www.cocoadev.com/index.pl?tollfreebridging">бесплатный мост</a> .</p><p> <a href="http://developer.apple.com/library/ios/#documentation/corefoundation/reference/cfarrayref/reference/reference.html"><code>cfarrayref</code></a> (и его изменяемый аналог <code>cfmutablearrayref</code>) предоставляют разработчику больше гибкости при создании объекта массива. смотрите четвертый аргумент назначенного инициализатора:</p><pre><code>cfarrayref cfarraycreate ( cfallocatorref allocator, const void **values, cfindex numvalues, const cfarraycallbacks *callbacks ); </code></pre><p>это позволяет вам запросить, чтобы объект <code>cfarrayref</code> использовал подпрограммы управления памятью core foundation, ни одной вообще или даже <em>ваши собственные</em> подпрограммы управления памятью.</p><p>обязательный пример:</p><pre><code>// one would pass &kcftypearraycallbacks (in lieu of null) if using cf types. cfmutablearrayref arrayref = cfarraycreatemutable(kcfallocatordefault, 0, null); nsmutablearray *array = (nsmutablearray *)arrayref; struct {int member;} mystruct = {.member = 42}; // casting to "id" to avoid compiler warning [array addobject:(id)&mystruct]; // hurray! struct {int member;} *mysamestruct = [array objectatindex:0]; </code></pre><p>приведенный выше пример полностью игнорирует проблемы, связанные с управлением памятью. структура <code>mystruct</code> создается в стеке и, следовательно, уничтожается при завершении функции - массив будет содержать указатель на объект, которого больше нет. вы можете обойти это, используя свои собственные процедуры управления памятью - следовательно, почему эта опция предоставляется вам - но тогда вам придется выполнить тяжелую работу по подсчету ссылок, выделению памяти, освобождению ее и так далее.</p><p>я бы не рекомендовал это решение, но оставлю его здесь на случай, если оно заинтересует кого-либо еще. :-)</p><hr><p>использование вашей структуры, выделенной в куче (вместо стека), демонстрируется здесь:</p><pre><code>typedef struct { float w, x, y, z; } megapoint; // one would pass &kcftypearraycallbacks (in lieu of null) if using cf types. cfmutablearrayref arrayref = cfarraycreatemutable(kcfallocatordefault, 0, null); nsmutablearray *array = (nsmutablearray *)arrayref; megapoint *mypoint = malloc(sizeof(megapoint); mypoint->w = 42.0f; // set ivars as desired.. // casting to "id" to avoid compiler warning [array addobject:(id)mypoint]; // hurray! megapoint *mysamepoint = [array objectatindex:0]; </code></pre>

<p>аналогичный метод для добавления структуры c состоит в том, чтобы сохранить указатель и снять указатель с него;</p><pre><code>typedef struct bstnode { int data; struct bstnode *leftnode; struct bstnode *rightnode; }bstnode; bstnode *rootnode; //declaring a nsmutablearray @property(nonatomic)nsmutablearray *queues; //storing the pointer in the array [self.queues addobject:[nsvalue value:&rootnode withobjctype:@encode(bstnode*)]]; //getting the value bstnode *frontnode =[[self.queues objectatindex:0] pointervalue]; </code></pre>

<p>если вы чувствуете себя неряшливо или действительно хотите создать много классов: иногда полезно динамически создать класс objc (ref: <code>class\_addivar</code>). таким образом, вы можете создавать произвольные классы objc из произвольных типов. вы можете указать поле за полем или просто передать информацию о структуре (но это практически репликация nsdata). иногда полезный, но, вероятно, более интересный факт для большинства читателей.</p><blockquote> <p>как бы я применил это здесь?</p> </blockquote><p>вы можете вызвать class\_addivar и добавить переменную экземпляра megapoint в новый класс, или вы можете синтезировать вариант objc класса megapoint во время выполнения (например, переменную экземпляра для каждого поля megapoint).</p><p>первый эквивалентен скомпилированному классу objc:</p><pre><code>@interface monmegapoint { megapoint megapoint; } @end </code></pre><p>последний эквивалентен скомпилированному классу objc:</p><pre><code>@interface monmegapoint { float w,x,y,z; } @end </code></pre><p>после того, как вы добавили ivars, вы можете добавлять/синтезировать методы.</p><p>чтобы прочитать сохраненные значения на принимающей стороне, используйте ваши синтезированные методы <code>object\_getinstancevariable</code> или <code>valueforkey:</code> (которые часто преобразуют эти скалярные переменные экземпляра в представления nsnumber или nsvalue).</p><p>кстати: все ответы, которые вы получили, полезны, некоторые лучше/хуже/недействительны в зависимости от контекста/сценария. конкретные потребности, касающиеся памяти, скорости, простоты обслуживания, простоты передачи или архивирования и т. д., определят, что лучше для данного случая ... но не существует «идеального» решения, которое идеально во всех отношениях. нет «лучшего способа поместить c-структуру в nsarray», просто «лучший способ поместить c-структуру в nsarray для<em> конкретного сценария, случая или набора требований </em>», - который вы должны будете указать.</p><p>более того, nsarray - это обычно повторно используемый интерфейс массива для типов с указателем (или меньшего размера), но существуют другие контейнеры, которые лучше подходят для c-структур по многим причинам (std :: vector является типичным выбором для c-структур).</p>

<p>было бы лучше использовать сериализатор objc бедняка, если вы делитесь этими данными между несколькими abis/архитектурами:</p><pre><code>megapoint mpt = /* ... */; nsmutabledictionary * d = [nsmutabledictionary new]; assert(d); /* optional, for your runtime/deserialization sanity-checks */ [d setvalue:@"megapoint" forkey:@"type-identifier"]; [d setvalue:[nsnumber numberwithfloat:mpt.w] forkey:@"w"]; [d setvalue:[nsnumber numberwithfloat:mpt.x] forkey:@"x"]; [d setvalue:[nsnumber numberwithfloat:mpt.y] forkey:@"y"]; [d setvalue:[nsnumber numberwithfloat:mpt.z] forkey:@"z"]; nsarray *a = [nsarray arraywithobject:d]; [d release], d = 0; /* ... */ </code></pre><p>... особенно если структура может меняться со временем (или в зависимости от целевой платформы). это не так быстро, как другие варианты, но с меньшей вероятностью сломается в некоторых условиях (которые вы не указали как важные или нет).</p><p>если сериализованное представление не выходит из процесса, то размер/порядок/выравнивание произвольных структур не должны изменяться, и есть варианты, которые проще и быстрее.</p><p>в любом случае вы уже добавляете объект с подсчетом ссылок (по сравнению с nsdata, nsvalue), так что ... создание класса objc, содержащего megapoint, является правильным ответом во многих случаях.</p>

<p>хотя использование nsvalue прекрасно работает для хранения структур в виде объекта obj-c, вы не можете кодировать nsvalue, содержащую структуру, с помощью nsarchiver/nskeyedarchiver. вместо этого вы должны кодировать отдельные элементы структуры ...</p><p>см. <a href="http://developer.apple.com/library/ios/documentation/cocoa/conceptual/archiving/articles/codingctypes.html#//apple\_ref/doc/uid/20001294-96941-bbcdihef">руководство по программированию архивов и сериализаций apple> структуры и битовые поля</a> </p>

<p>вместо того, чтобы пытаться поместить структуру c в nsarray, вы можете поместить их в nsdata или nsmutabledata как массив структур c. чтобы получить к ним доступ, вы должны сделать</p><pre><code>const struct mystruct * thestruct = (const struct mystruct*)[mydata bytes]; int value = thestruct[2].integernumber; </code></pre><p>или установить тогда</p><pre><code>struct mystruct * thestruct = (struct mystruct*)[mydata mutablebytes]; thestruct[2].integernumber = 10; </code></pre>

<p>я предлагаю вам использовать std :: vector или std :: list для типов c/c++, потому что сначала это просто быстрее, чем nsarray, а во-вторых, если вам не хватит скорости - вы всегда можете создать свой собственный распределители для контейнеров stl и сделать их еще быстрее. все современные мобильные игровые, физические и звуковые движки используют контейнеры stl для хранения внутренних данных. просто потому что они действительно быстрые.</p><p>если это не для вас - есть хорошие ответы от ребят о nsvalue - я думаю, что это наиболее приемлемо.</p>