gpt4 book ai didi

cocoa - cocoa /ARC-为什么将结构作为参数传递会导致对自动释放池的错误访问?

转载 作者:行者123 更新时间:2023-12-03 16:58:52 24 4
gpt4 key购买 nike

我发现项目在模拟器上运行良好后,刚刚完成了项目的调试工作,但是在使用ARC在@autoreleasepool上出现EXC BAD ACCESS错误的设备上进行测试时,它会崩溃。

最终,我将问题缩小到我创建的自定义类的自定义init方法的位置,该自定义类接受两个结构作为参数。这些结构基于相同的定义,仅包含3个GLfloat,用于表示x,y和z数据,用于定位和旋转。

当我修改自定义init方法以代替接受6个GLfloats而不是两个分别包含3个GLfloats的结构时,然后让init方法将这些GLfloats分配给该类的两个适当的实例变量结构,而不是将先前传递的结构直接分配给实例变量结构,一切正常,没有错误。

对于一些澄清:

我有一个这样定义的结构:

struct xyz{
GLfloat x;
GLfloat y;
GLfloat z;
};


然后,我有两个自定义类(称为foo)的ivars,称为位置和旋转,均基于此结构。

然后,我将定制的init方法简化为:

-(id) initWithPosition: (struct xyz) setPosition rotation: (struct xyz) setRotation
{
self = [super init];
if(self) {
position = setPosition;
rotation = setRotation;
}
return self;
}


我使用类似于以下内容的方式进行了调用:

struct xyz position;
struct xyz rotation;

// Fill position / rotation with data here....

foo *bar = [[foo alloc] initWithPosition: position rotation: rotation];


这导致了EXC不良访问。因此,我将init方法更改为:

-(id) initWithPositionX: (GLfloat) xp Y: (GLfloat) yp Z: (GLfloat) zp RotationX: (GLfloat) xr Y: (GLfloat) yr Z: (GLfloat) zr
{
self = [super init];
if(self) {
position.x = xp;
position.y = yp;
position.z = zp;

rotation.x = xr;
rotation.y = yr;
rotation.z = zr;

}
return self;
}


而且...一切都很好。

我的意思是,我很高兴自己“修复”了它,但是我不确定为什么要修复它。我已经读过ARC在使用对象的结构方面存在问题,但是我的结构仅由简单的GLfloat组成,不是对象……对吗?我宁愿知道为什么这仍然行得通,然后再希望其他人也遇到同样的问题。

谢谢,
-亚当·埃斯费尔德

编辑:添加源

尝试尝试一下,我似乎无法在测试项目中重复该问题,所以我的代码一定存在问题,正如其他人所建议的那样,我根本没有解决问题,只是将其掩盖到以后,这就是我的想法很害怕。

因此,如果任何人都可以看一下这段代码以指出这里可能出了什么问题(如果问题确实在init方法中,也许不是),我将不胜感激。它相当大,所以我理解是否没有人感兴趣,但是我已经对此进行了评论,并且会在代码片段之后详细解释发生了什么:

-(id) initWithName: (NSString*) setName fromFile: (NSString*) file
{
self = [super init];
if(self) {

//Initialize ivars
name = setName;
joints = [[NSMutableArray alloc] init];
animations = [[NSMutableArray alloc] init];
animationCount = 0;
frameCount = 0;

//Init file objects for reading
NSError *fileError;
NSStringEncoding *encoding;

//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:@"dat"] usedEncoding:encoding error:&fileError];

//Create a new NGLMaterial to apply to all of the loaded models from the file
NGLMaterial *material = [[NGLMaterial alloc] init];
material = [NGLMaterial material];
material.diffuseMap = [NGLTexture texture2DWithFile:@"resources/models/diffuse.bmp"];

//Check for nil file data
if(fileData == nil)
{
NSLog(@"Error reading mesh file");
}
else
{
//Separate the NSString of the file's contents into an array, one line per indice
NSMutableArray *fileLines = [[NSMutableArray alloc] initWithArray:[fileData componentsSeparatedByString:@"\n"] copyItems: YES];

//Create a pseudo counter variable
int i = -1;

//Allocate a nul NSString for looping
NSString *line = [[NSString alloc] initWithFormat:@""];

//Loop through each of the lines in the fileLines array, parsing the line's data
for (line in fileLines) {

//Increase the pseudo counter variable to determine what line we're currently on
i++;

if (i == 0) {
//The first line of the file refers to the number of animations for the player
animationCount = [line intValue];
}else
{

if (i == [fileLines count]-2) {
//The last line of the file refers to the frame count for the player
frameCount = [line intValue];

}else
{
//The lines inbetween the first and last contain the names of the .obj files to load

//Obtain the current .obj path by combining the name of the model with it's path
NSString *objPath = [[NSString alloc] initWithFormat:@"resources/models/%@.obj", line];

//Instantiate a new NGLMesh with the objPath NSString
NGLMesh *newMesh = [[NGLMesh alloc] initWithOBJFile:objPath];

//Apply various settings to the mesh such as material
newMesh.material = material;
newMesh.rotationOrder = NGLRotationOrderZYX;

//Compile the changes to the mesh
[newMesh compileCoreMesh];

//Add the mesh to this player's joints array
[joints addObject:newMesh];

//Read the animation data for this joint from it's associated file
NSLog(@"Reading animation data for: %@", line);

//The associated animation file for this model is found at (model's name).anim
NSString *animData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:line ofType:@"anim"] usedEncoding:encoding error:&fileError];

//Check for nil animation data
if(animData == nil)
{
NSLog(@"Error reading animation file");
}
else
{

//Construct temporary position and rotation structs to store the read xyz data from each frame of animation
struct xyz position;
struct xyz rotation;

//Create a new scanner to scan the current animation file for it's xyz position / rotation data per frame
NSScanner *scanner = [[NSScanner alloc] initWithString:animData];
while([scanner isAtEnd] == NO)
{

//Extract position data
[scanner scanFloat:&position.x];
[scanner scanFloat:&position.y];
[scanner scanFloat:&position.z];

//Extract rotation data
[scanner scanFloat:&rotation.x];
[scanner scanFloat:&rotation.y];
[scanner scanFloat:&rotation.z];



//OLD CODE NOT WORKING:
//AEFrame *frame = [[AEFrame alloc] initWithPosition: position rotation: rotation];

//Initialize new frame instance using new working init method
AEFrame *frame = [[AEFrame alloc] initWithPositionX:position.x Y:position.y Z:position.z RotationX:rotation.x Y:rotation.y Z:rotation.z];

//Add the created frame instace to the player's animations array
[animations addObject:frame];

}
}
}
}
}
}
}
return self;
}


好吧,基本上,我正在编写用于处理引擎中给定玩家的3D关节动画的代码。我在MEL中为Maya编写了一个脚本,该脚本允许您在视口中选择一系列动画模型(在本例中为机器人角色IE的上下臂,头部,躯干等的关节),然后运行脚本,它将遍历每个选定的模型并导出.anim文件。此.anim文件包含该文件在动画的每一帧处所指向的关节的xyz位置和旋转,因此其结构如下:

Frame 1 X Position
Frame 1 Y Position
Frame 1 Z Position
Frame 1 X Rotation
Frame 1 Y Rotation
Frame 1 Z Rotation
Frame 2 X Position
Frame 2 Y Position
etc...


但是,此动画文件仅由引用xyz位置和旋转的浮点数组成,没有实际的文本标记每行,如上所示,仅用于参考。

为动画角色的每个选定关节完成.anim文件的导出后,脚本将导出一个扩展名为.dat的最终文件。该文件包含导出的动画数量(此值是在脚本中设置的,例如,您可能将3个动画导出到.anim文件,例如Run,Walk和Idle),然后包含名称列表。这些名称既指要加载到3D引擎中的.obj文件的名称,也指要为已加载的.obj文件加载的相应.anim文件。

在解释了这一点之后,我将介绍我在项目中如何处理此问题:


用户使用上述方法-initWithName实例化一个新的“ AEPlayer”类:File:
此方法首先打开从maya导出的.dat文件,以查找要加载到播放器关节的文件的名称。
对于.dat文件中找到的每个关节名称,系统将继续使用该关节名称(在其末尾带有“ .obj”)实例化一个新的NGLMesh实例,然后打开当前关节的.anim文件。系统还将实例化的NGLMesh添加到玩家的关节数组中。
对于每个关节的.anim文件,它使用NSScanner读取文件。它一次扫描6行(每帧的xyz位置和xyz旋转)。扫描一帧数据后,它实例化一个称为AEFrame的类,并将帧的位置数据设置为加载的位置数据,并将旋转数据设置为加载的旋转数据。这是我无法在其init参数中传递xyz位置结构和xyz旋转结构的类,但是通过传入6个GLfloat来“固定”,如上面的代码所示。
实例化帧后,该帧将添加到当前播放器的动画数组(NSMutableArray)中。这个动画数组最终变得相当大,因为它存储了模型中每个关节的所有动画数据。我正在测试的模型具有42个关节,每个关节具有12帧动画,每个帧具有6个GLfloat。
系统完成将.dat文件中找到的所有.obj文件及其关联的.obj和.anim文件加载到内存中时,即完成。
在程序的主运行循环中,我仅循环遍历所有创建的AEPlayer,对于每个AEPLayer,我遍历玩家的关节数组,将存储在当前玩家关节处的NGLMesh的位置和旋转设置为任意位置/旋转在当前关节的玩家动画数组中找到。


我意识到让所有人阅读所有内容都花了很长时间,但是我认为我应该把它扔在那里。另外,我知道上面的代码片段中有一些无效分配需要删除,但它们并没有助长问题(在出现问题后我添加了它们,以查看使用Instruments进行明智的内存存储) 。

非常感谢您的帮助,
-亚当·埃斯费尔德

最佳答案

其他代码可能破坏了堆栈,或执行了其他未定义的操作。如果您正在执行类似的操作(调试起来可能非常棘手),那么所有的选择都将关闭,简单的更改可能会掩盖或掩盖真正的错误。

当您在设备和模拟器之间进行切换时,您还将针对ARM而不是i386进行构建,并且针对优化而不是调试进行构建。这些都是巨大的变化,并且可能会改变未定义行为的结果。例如,在数组末尾写入一个字节可能不会对调试版本造成任何损害,但会对发行版本造成灾难性的失败。将结构更改为浮点数可能会更改将参数传递到方法中的方式,从而可能掩盖或取消掩盖错误。

您应该尝试将问题隔离到一个小样本项目中。例如,您是否可以仅使用一个init方法来重现该问题?

如果您还不熟悉此主题,那么LLVM博客中的这组文章可能会有所帮助:


http://blog.llvm.org/2011/05/what-every-c-programmer-should-know.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_14.html
http://blog.llvm.org/2011/05/what-every-c-programmer-should-know_21.html


更新:

看来您的问题可能在这里:

NSStringEncoding *encoding;
//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:@"dat"] usedEncoding:encoding error:&fileError];


-[NSString initWithContentsOfFile:usedEncoding:error]的usedEncoding参数是out参数,这意味着您提供存储并将指针传递给该存储,该函数将向该存储写入一些输出。您提供了一个指针,但是它没有指向任何有效的指针,并且-[NSString initWithContentsOfFile:usedEncoding:error]写入usedEncoding指针时,它将覆盖某些内容。

您应该将该代码更改为此:

NSStringEncoding encoding;
//Load the specified file's contents into an NSString
NSString *fileData = [[NSString alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:file ofType:@"dat"] usedEncoding:&encoding error:&fileError];


现在,您提供了一个指向有效字符串编码变量的指针。

关于cocoa - cocoa /ARC-为什么将结构作为参数传递会导致对自动释放池的错误访问?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8814392/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com