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

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

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

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

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

(Это вызывает интересную проблему: есть ли быстрая библиотека c, в отличие от STL::vector но намного легче, что позволяет минимально «аккуратно обрабатывать массивы» …?)

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

Например:

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

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

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

Кстати, это подход 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]; 

BTW как точка отсчета и благодаря Jarret Hardie, вот как хранить CGPoints и подобные в NSArray :

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

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

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

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

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

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

 Megapoint p; [value getValue:&p]; 

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

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

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

Это позволяет запросить, CFArrayRef объект 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]; 

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

Как я могу применить это здесь?

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

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

 @interface MONMegapoint { Megapoint megapoint; } @end 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 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; 

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

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

Объект Obj C – это просто структура C с добавленными элементами. Поэтому просто создайте пользовательский class, и у вас будет тип структуры C, который требуется NSArray. Любая структура C, которая не имеет дополнительного крутизны, который NSObject включает в свою структуру C, будет неудобоваримой для NSArray.

Использование NSData в качестве оболочки может содержать только копию структур, а не исходных структур, если это имеет для вас значение.

Вы можете использовать classы NSObject, отличные от C-Структур, для хранения информации. И вы можете легко сохранить этот NSObject в NSArray.

  • Учебники по использованию HTTP POST и GET на iPhone в Objective-C
  • Увеличить значок Push Push iPhone
  • Добавление файлов в отдельные объекты в Xcode 4
  • Зачем писать 1000 000 000 как 1000 * 1000 * 1000 в C?
  • @class против #import
  • Как получить столбец datetime в SQLite с Objective C
  • Использование alloc init вместо нового
  • Как предотвратить циклическую ссылку, когда Swift-заголовок заголовка импортирует файл, который импортирует сам Hopscotch-Swift.h
  • Класс эквивалентен методу -ответчик для выбора:
  • Должен ли я использовать свойства или прямую ссылку при доступе к переменным экземпляра внутри?
  • Верно ли, что NS64 () не должен использовать производственный код?
  • Interesting Posts

    OWIN – Authentication.SignOut (), похоже, не удаляет файл cookie

    Каков эффективный способ реализации одноэлементного шаблона в Java?

    Как подсчитать уникальные элементы в поле в запросе Access?

    Случайность Java всегда возвращает тот же номер, когда я устанавливаю семя?

    Установка ОС по сети

    Как написать json с детьми из R

    Панель инструментов Android Добавление элементов меню для разных fragmentов

    Как изменить файлы конфигурации .NET во время установки?

    Я продолжаю получать BSOD. Как я могу отслеживать, какое приложение / драйвер вызывает его?

    Без подключения к Интернету после обновления до Windows 10

    Размещение компонента на стеклянной панели

    Эквивалент Firebug «Скопировать XPath» в Internet Explorer?

    Как передать смарт-экран на Win8 при установке подписанного приложения?

    Перечислите или перечислите все переменные в программе

    Как написать правильный нуль-безопасный коалесцирующий оператор в scala?

    Давайте будем гением компьютера.