在NSArray中放置一个c-struct的最好方法是什么?

NSArray存储c结构的常用方法是什么? 内存处理的优点,缺点?

值得注意的是, valueWithBytesvalueWithPointer什么区别 – 由下面的justin和catfish引发。

下面是苹果公司关于valueWithBytes:objCType:的讨论的链接 valueWithBytes:objCType:面向未来的读者

对于一些横向思考和更多的performance,Evgen提出了在C ++中使用STL::vector的问题。

(这提出了一个有趣的问题:是否有一个快速的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]; 

顺便说一句,作为一个参考点,并感谢Jarret Hardie,以下是如何在NSArray存储CGPoints和类似:

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

(请参阅如何以简单的方式将CGPoint对象添加到NSArray? )

NSValue不仅支持CoreGraphics结构 – 你也可以自己使用它。 我build议这样做,因为类可能比NSData轻简单的数据结构。

简单地使用如下的expression式:

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

为了获得价值:

 Megapoint p; [value getValue:&p]; 

我build议你坚持NSValue路线,但如果你真的希望在你的NSArray(和Cocoa中的其他集合对象)中存储普通的'ol struct数据types,你可以这样做 – 尽pipe间接地使用Core Foundation和Toll-免费桥接 。

CFArrayRef (及其可变对象CFMutableArrayRef )在创build数组对象时为开发人员提供了更大的灵活性。 看到指定的初始化程序的第四个参数:

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

这允许您请求CFArrayRef对象使用Core Foundation的内存pipe理例程,根本不使用内存pipe理例程,甚至使用自己的内存pipe理例程。

强制性的例子:

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

上面的例子完全忽略了关于内存pipe理的问题。 结构myStruct是在堆栈上创build的,因此在函数结束时被销毁 – 数组将包含一个指向不再存在的对象的指针。 你可以通过使用自己的内存pipe理例程来解决这个问题 – 为什么这个选项是提供给你的 – 但是你必须做引用计数,分配内存,重新分配等等。

我不会推荐这个解决scheme,但会保留在这里,以防其他人感兴趣。 🙂


在堆中使用您的结构(代替堆栈)将在此处演示:

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

如果你感到讨厌,或者真的有很多类需要创build:dynamic构造一个objc类(ref: class_addIvar )是偶尔有用的。 这样,你可以从任意types创build任意的objc类。 你可以指定字段的字段,或者只是传递结构的信息(但实际上复制NSData)。 有时是有用的,但对于大多数读者来说可能更多是一个“有趣的事实”。

我将如何在这里应用这个?

您可以调用class_addIvar并将一个Megapoint实例variables添加到新类中,也可以在运行时(例如Megapoint的每个字段的实例variables)合成Megapoint类的objc变体。

前者相当于编译的objc类:

 @interface MONMegapoint { Megapoint megapoint; } @end 

后者相当于编译的objc类:

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

添加完ivars之后,可以添加/合成方法。

要读取接收端存储的值,使用你的综合方法, object_getInstanceVariablevalueForKey:通常会将这些标量实例variables转换成NSNumber或NSValue表示)。

顺便说一句:所有你收到的答案是有用的,有些是更好/更糟糕/无效取决于上下文/情况。 关于记忆,速度,易维护性,易于转移或归档等方面的具体需求将决定哪一个对于特定情况是最好的,但是没有任何理想的“完美”解决scheme。 没有'把一个c-struct放在一个NSArray中的最好方法',只是一个'把一个c-struct放到一个NSArray中用于特定场景,情况或者一组需求的最好方法'指定。

此外,NSArray是一个通常可重用的指针大小(或更小)types的数组接口,但也有其他容器更适合于c-structs,原因很多(std :: vector是c-structs的典型select)。

如果您在多个abis /架构上共享这些数据,最好使用穷人的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; /* ... */ 

特别是如果结构可以随时间变化(或通过目标平台)。 它并不像其他选项那么快,但是在某些情况下(您没有指定重要或不重要),它不太可能中断。

如果序列化表示没有退出进程,那么任意结构的大小/顺序/alignment不应该改变,并且有更简单和更快的选项。

在任何情况下,你已经添加一个ref-counting对象(与NSData,NSValue比较),所以…创build一个objc类,它包含Megapoint在许多情况下是正确的答案。

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

我build议你为C / C ++types使用std :: vector或std :: list,因为起初它比NSArray快,而且如果没有足够的速度,你总是可以创build自己的为STL容器分配器,并使它们更快。 所有现代移动游戏,物理和audio引擎都使用STL容器来存储内部数据。 只因为他们真的很快

如果不适合你 – 有关NSValue的好消息 – 我认为这是最可以接受的。

而不是试图将c结构放在一个NSArray中,你可以把它们放在NSData或NSMutableData中作为结构的ac数组。 要访问他们,你会做的

 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对象,但不能使用NSArchiver / NSKeyedArchiver对包含结构的NSValue进行编码。 相反,你必须编码单独的结构成员…

请参阅Apple的档案和序列化编程指南>结构和位域

你可以在NSArray中存储实现NSObject协议的任何东西。 我通常只是创build自己的类,包装C结构,并inheritanceNSObject(因此自动实现NSObject协议),然后使用它。

Obj C对象只是一个带有一些添加元素的C结构体。 所以只需创build一个自定义的类,并且您将拥有NSArray所需的C结构types。 任何没有NSObject包含在其C结构中的额外的C结构的C结构对于NSArray来说是不可能消化的。

使用NSData作为包装可能只存储结构的副本,而不是原始结构,如果这对您有所帮助。

您可以使用C结构以外的NSObject类来存储信息。 你可以很容易地将NSObject存储到NSArray中。