编码/解码结构对象

时间:2013-06-25 13:22:31

标签: iphone objective-c opengl-es struct

我正在研究这个wavefront obj加载器示例:https://github.com/jlamarche/iOS-OpenGLES-Stuff/tree/master/Wavefront%20OBJ%20Loader

为了提高对象加载速度 - 我决定将加载的对象编码为NSValue,然后存储在Core数据中。之后,当需要再次显示相应的对象时 - 我从数据库中获取必要的nsvalue并取消它。

一切正常,但问题是 - 它没有那么快,我希望它会如此。

其中一个原因 - 因为,在该示例中使用了struct对象。

我没有成功对它们进行编码/解码,所以我使用了NSMutableArray,其中我编写了所有结构数据,后来 - 我遍历它以将值放回到struct对象。

例如,有struct:

typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
} Vertex3D;

然后定义:

typedef Vertex3D Vector3D;

但在OpenGLWaveFrontObject类中,它的使用方式如下:

Vector3D *vertexNormals;    

我怎么能像这样编码/解码struct?

我试过这样:

[coder encodeObject:[NSValue value:&vertexNormals withObjCType:@encode(Vector3D)] forKey:@"vertexNormals"];

或者像这样:

[coder encodeObject:[NSValue value:&vertexNormals withObjCType:@encode(Vector3D[30])] forKey:@"vertexNormals"];

但它不起作用 - 如果成功编码,则在解码时 - 值不正确。

示例,我如何放回struct必需值的数组:

    vertices = malloc(sizeof(Vertex3D) *  [verticesArray count]);

    for(int i = 0; i < [verticesArray count]; i++)
    {            
        vertices[i].x = [[[verticesArray objectAtIndex:i] objectAtIndex:0] floatValue];

        vertices[i].y = [[[verticesArray objectAtIndex:i] objectAtIndex:1] floatValue];

        vertices[i].z = [[[verticesArray objectAtIndex:i] objectAtIndex:2] floatValue];
    }

这是有效的,但我有更多的结构数组,如果数组很大 - 这成为一个大问题。

修改

使用 Amin Negm-Awad 提供了答案:

如果我只是将NSArchiver更改为NSKeyedArchiver - 它会抛出错误:

[NSKeyedArchiver encodeArrayOfObjCType:count:at:]: unsupported type "{?=fff}]" for array encoding' 

当我尝试:

NSValue *encapsulated = [NSValue valueWithBytes:vertexNormals objCType:@encode(Vertex3D[3])]; 
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:encapsulated]; 
[coder encodeObject:data forKey:@"vertexNormals"]; 

创建了vertexNormals:

vertexNormals = calloc(3, sizeof(Vector3D)); 

并填写了您提供的类似数据。

编辑2:

使用Amin Negm-Awad提供的更新答案,

我成功地将struct对象存储为NSData并对其进行编码,然后对其进行解码。它有效!

例如,它也可以帮助其他人:

//Encode
NSData *verticesData = [NSData dataWithBytes:vertices length:numberOfVertices * sizeof(Vector3D)];

[coder encodeObject:verticesData forKey:@"vertices"];


//Decode
vertices = malloc(sizeof(Vertex3D) * numberOfVertices);

NSData *verticesData = [decoder decodeObjectForKey:@"vertices"];

[verticesData getBytes:vertices length:sizeof(Vertex3D) *  numberOfVertices];

其中Vertex3D是一个结构:

typedef struct {
GLfloat x;
GLfloat y;
GLfloat z;
} Vertex3D;

并用作结构数组(我不知道它是如何被调用的):

Vertex3D *vertices;

不幸的是我无法存储纹理数据。我可以编码和解码,但解码数据总是随机奇怪。

我以这种方式宣布:

GLfloat *textureCoords;

我以这种方式编码/解码:

//Encode
NSData *textureCoordsData = [NSData dataWithBytes:textureCoords length:valuesPerCoord * numberOfVertices];

[coder encodeObject:textureCoordsData forKey:@"textureCoords"];

//Decode
textureCoords = malloc(sizeof(GLfloat) * valuesPerCoord * numberOfVertices);

NSData *textureCoordsData = [decoder decodeObjectForKey:@"textureCoords"];

[textureCoordsData getBytes:textureCoords length:valuesPerCoord * numberOfVertices];

可能是什么问题?

1 个答案:

答案 0 :(得分:1)

首先:如果数组具有可变长度,则根本无法使用NSValue。这是documented

  

您指定的类型必须是恒定长度。你不能存储C.   字符串,可变长度数组和结构,以及其他数据类型   在NSValue中的不确定长度 - 你应该使用NSString或   这些类型的NSData对象。

所以你应该考虑使用NSData。

但是,如果你想使用NSValue(因为你有一个恒定长度的数组):

您的两种方法都有额外的间接性。第一个也有错误的类型。应该做些什么:

// Create some data
Vertex3D *cArray = malloc(30 * sizeof(Vertex3D));
NSInteger i;
for (i=0; i<30; i++)
{
   cArray[i].x = i;
   cArray[i].y = 9811;
   cArray[i].z = 29-i;
}
NSLog(@"%p", cArray);

// Encapsulate that in a value
// Have a look at the parameters
NSValue *encapsulated = [NSValue valueWithBytes:cArray objCType:@encode(Vertex3D[30])];

// Put it through a coder and store the coded data on disk
NSData *data = [NSArchiver archivedDataWithRootObject:encapsulated];
[data writeToFile:[@"~/Desktop/valueTest" stringByExpandingTildeInPath] atomically:YES];
// Out is done here

// from disk
data = [NSData dataWithContentsOfFile:[@"~/Desktop/valueTest" stringByExpandingTildeInPath]];
encapsulated = [NSUnarchiver unarchiveObjectWithData:data];

Vertex3D *cArray2 = malloc(30 * sizeof(Vertex3D));
[encapsulated getValue:cArray2];
for (i=0; i<30; i++)
{
   NSLog(@"%f %f %f", cArray[i].x, cArray[i].y, cArray[i].z);
}
NSLog(@"%p", cArray2);

这适用于我的测试代码

更新,因为键控效果存在问题: 要将c数组存储在NSData实例中而不是NSValue实例中,请执行此操作

NSLog(@"%p", cArray);

将代码更改为

// Convert to an NSData instance
NSData *data = [NSData dataWithBytes:cArray length:lengthInBytes];
[data writeToFile:[@"~/Desktop/valueTest" stringByExpandingTildeInPath] atomically:YES];
// Out is done here

// from disk
data = [NSData dataWithContentsOfFile:[@"~/Desktop/valueTest" stringByExpandingTildeInPath]];
Vertex3D *cArray2 = malloc(lengthInBytes);
[data getBytes:cArray2 length:lengthInBytes];

,直到

for (i=0; i<30; i++)