【问题标题】:Encode/Decode struct object编码/解码结构对象
【发布时间】:2013-06-25 13:22:31
【问题描述】:

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

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

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

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

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

比如有struct:

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

然后定义:

typedef Vertex3D Vector3D;

但是在 OpenGLWaveFrontObject 类中它是这样使用的:

Vector3D *vertexNormals;    

我怎样才能像这样对结构进行编码/解码?

我试过这样:

[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 = calloc(3, sizeof(Vector3D)); 

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

编辑 2:

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

我成功地将结构对象存储为 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;

并用作struct数组(我不知道它是如何真正调用的):

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

可能是什么问题?

【问题讨论】:

  • 您要编码单个结构还是该结构的数组? “不工作”是什么意思?
  • 结构数组。不工作 - 就像 - 如果成功编码,那么解码时随机不正确的值。

标签: iphone objective-c opengl-es struct


【解决方案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);

这适用于我的测试代码

更新因为keyed实现的问题: 要将 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++)

【讨论】:

  • 它向我显示“使用未声明的标识符'NSArchiver'”并且它不适用于 NSKeyedArchiver(显示 - [NSKeyedArchiver encodeValueOfObjCType:at:]:数组计数丢失或为零')我读到 NSArchiver 比 NSKeyedArchiver 旧,所以 - sdk 4.6 (iOS6.1) 是否可能不再支持它?
  • 哦,抱歉,我在 Mac OS 上测试了该代码。但这并不重要,你如何编码/解码它。这只是周围的测试代码(并且您的实例可能在对象图中很深,因此您的代码看起来仍然不同)。只需将 NSArchiver 更改为 NSKeyedArchiver 并将 NSUNarchiver 更改为 NSKeyedUnarchiver。但这并不重要。重要的是,参数 valueWithBytes: 没有额外的间接。查看注释后面的行:“将其封装在一个值中”。
  • 哦,是的,keyed archiver 不支持这个。我不知道。所以请改用 NSData。
  • 请查看我更新的问题。感谢您,我能够编码/解码结构数组,但我仍然需要一件事 - 编码/解码 CGFloat 数组? (或如何称呼)。请参阅编辑 2。到目前为止,感谢您的帮助!!!
  • 好的,无论如何-如果您不能进一步帮助我-您仍然帮助我解决了原始问题,因此我将其标记为已接受并+1以提供帮助!再次感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-20
  • 2018-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-11
相关资源
最近更新 更多