it-swarm.com.ru

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

Какой обычный способ хранить c-структуры в NSArray? Преимущества, недостатки, обработка памяти?

Примечательно, в чем разница между valueWithBytes и valueWithPointer - поднятые Джастином и сомом ниже.

Вот ссылка на обсуждение Apple valueWithBytes:objCType: для будущих читателей ...

Для некоторого бокового мышления и больше внимания к производительности, Евгений поднял вопрос об использовании STL::vector в C++.

(В связи с этим возникает интересная проблема: существует ли быстрая библиотека c, похожая на STL::vector, но намного более легкая, что обеспечивает минимальную "аккуратную обработку массивов" ...?)

Итак, оригинальный вопрос ...

Например:

typedef struct _Megapoint {
    float   w,x,y,z;
} Megapoint;

Итак: каков нормальный, лучший идиоматический способ хранения собственной структуры, такой как эта, в NSArray, и как вы обращаетесь с памятью в этой идиоме?

Обратите внимание, что я специально ищу обычную идиому для хранения структур. Конечно, можно избежать этой проблемы, создав новый маленький класс. Однако я хочу знать, как обычная идиома для фактического размещения структур в массиве, спасибо.

Кстати, вот подход NSData, который возможно? не самый лучший ...

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];

Кстати, в качестве ориентира и благодаря Джаррету Харди, вот как хранить CGPoints и аналогичные в NSArray:

NSArray *points = [NSArray arrayWithObjects:
        [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
        [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)],
        nil];

(см. Как я могу легко добавить объекты CGPoint в NSArray? )

84
Fattie

NSValue не только поддерживает структуры CoreGraphics - вы также можете использовать его для своих собственных. Я бы порекомендовал это сделать, поскольку класс, вероятно, имеет меньший вес, чем NSData для простых структур данных.

Просто используйте выражение, подобное следующему:

[NSValue valueWithBytes:&p objCType:@encode(Megapoint)];

И чтобы вернуть значение:

Megapoint p;
[value getValue:&p];
153
Justin Spahr-Summers

Я бы посоветовал вам придерживаться маршрута NSValue , но если вы действительно хотите сохранить типы данных 'ol struct в вашем NSArray (и других объектах коллекции в Какао), вы можете сделать это, хотя и косвенно, используя Core Фундамент и бесплатный мост .

CFArrayRef (и его изменяемый аналог CFMutableArrayRef) предоставляют разработчику больше гибкости при создании объекта массива. Смотрите четвертый аргумент назначенного инициализатора:

CFArrayRef CFArrayCreate (
    CFAllocatorRef allocator,
    const void **values,
    CFIndex numValues,
    const CFArrayCallBacks *callBacks
);

Это позволяет вам запросить, чтобы объект CFArrayRef использовал подпрограммы управления памятью Core Foundation, ни одной вообще или даже ваши собственные подпрограммы управления памятью.

Обязательный пример:

// 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];

Приведенный выше пример полностью игнорирует проблемы, связанные с управлением памятью. Структура myStruct создается в стеке и, следовательно, уничтожается при завершении функции - массив будет содержать указатель на объект, которого больше нет. Вы можете обойти это, используя свои собственные процедуры управления памятью - следовательно, почему эта опция предоставляется вам - но тогда вам придется выполнить тяжелую работу по подсчету ссылок, выделению памяти, освобождению ее и так далее.

Я бы не рекомендовал это решение, но оставлю его здесь на случай, если оно заинтересует кого-либо еще. :-)


Использование вашей структуры, выделенной в куче (вместо стека), демонстрируется здесь:

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];
7
Sedate Alien

Аналогичный метод для добавления структуры c состоит в том, чтобы сохранить указатель и снять указатель с него;

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];
4
Keynes

если вы чувствуете себя неряшливо или действительно хотите создать много классов: иногда полезно динамически создать класс objc (ref: class_addIvar). таким образом, вы можете создавать произвольные классы objc из произвольных типов. Вы можете указать поле за полем или просто передать информацию о структуре (но это практически репликация NSData). иногда полезный, но, вероятно, более интересный факт для большинства читателей.

Как бы я применил это здесь?

вы можете вызвать class_addIvar и добавить переменную экземпляра Megapoint в новый класс, или вы можете синтезировать вариант objc класса Megapoint во время выполнения (например, переменную экземпляра для каждого поля Megapoint).

первый эквивалентен скомпилированному классу objc:

@interface MONMegapoint { Megapoint megapoint; } @end

последний эквивалентен скомпилированному классу objc:

@interface MONMegapoint { float w,x,y,z; } @end

после того, как вы добавили ivars, вы можете добавлять/синтезировать методы.

чтобы прочитать сохраненные значения на принимающей стороне, используйте ваши синтезированные методы object_getInstanceVariable или valueForKey: (которые часто преобразуют эти скалярные переменные экземпляра в представления NSNumber или NSValue).

кстати: все ответы, которые вы получили, полезны, некоторые лучше/хуже/недействительны в зависимости от контекста/сценария. Конкретные потребности, касающиеся памяти, скорости, простоты обслуживания, простоты передачи или архивирования и т. д., определят, что лучше для данного случая ... но не существует «идеального» решения, которое идеально во всех отношениях. нет «лучшего способа поместить c-структуру в NSArray», просто «лучший способ поместить c-структуру в NSArray для конкретного сценария, случая или набора требований », - который Вы должны будете указать.

более того, NSArray - это обычно повторно используемый интерфейс массива для типов с указателем (или меньшего размера), но существуют другие контейнеры, которые лучше подходят для c-структур по многим причинам (std :: vector является типичным выбором для c-структур).

3
justin

было бы лучше использовать сериализатор objc бедняка, если вы делитесь этими данными между несколькими abis/архитектурами:

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;
/* ... */

... особенно если структура может меняться со временем (или в зависимости от целевой платформы). это не так быстро, как другие варианты, но с меньшей вероятностью сломается в некоторых условиях (которые вы не указали как важные или нет).

если сериализованное представление не выходит из процесса, то размер/порядок/выравнивание произвольных структур не должны изменяться, и есть варианты, которые проще и быстрее.

в любом случае вы уже добавляете объект с подсчетом ссылок (по сравнению с NSData, NSValue), так что ... создание класса objc, содержащего Megapoint, является правильным ответом во многих случаях.

3
justin

Хотя использование NSValue прекрасно работает для хранения структур в виде объекта Obj-C, вы не можете кодировать NSValue, содержащую структуру, с помощью NSArchiver/NSKeyedArchiver. Вместо этого вы должны кодировать отдельные элементы структуры ...

См. Руководство по программированию архивов и сериализаций Apple> Структуры и битовые поля

0
DarkMatter

Вместо того, чтобы пытаться поместить структуру c в NSArray, вы можете поместить их в NSData или NSMutableData как массив структур c. Чтобы получить к ним доступ, вы должны сделать

const struct MyStruct    * theStruct = (const struct MyStruct*)[myData bytes];
int                      value = theStruct[2].integerNumber;

или установить тогда

struct MyStruct    * theStruct = (struct MyStruct*)[myData mutableBytes];
theStruct[2].integerNumber = 10;
0
Nathan Day

Я предлагаю вам использовать std :: vector или std :: list для типов C/C++, потому что сначала это просто быстрее, чем NSArray, а во-вторых, если вам не хватит скорости - вы всегда можете создать свой собственный распределители для контейнеров STL и сделать их еще быстрее. Все современные мобильные игровые, физические и звуковые движки используют контейнеры STL для хранения внутренних данных. Просто потому что они действительно быстрые.

Если это не для вас - есть хорошие ответы от ребят о NSValue - я думаю, что это наиболее приемлемо.

0
Evgen Bodunov