Я только что закончил отлаживать ад своего проекта после того, как обнаружил, что он отлично работает на симуляторе, но вылетает при тестировании на устройстве с ошибкой EXC BAD ACCESS в @autoreleasepool с использованием ARC.
Я, наконец, сузил проблему до того места, где у меня был собственный метод init для пользовательского класса, который я создал, который принял две структуры в качестве параметров. Структуры основаны на том же определении и содержат только 3 GLfloats для представления данных x, y и z для использования с позиционированием и вращением.
Когда я изменил пользовательский метод init, чтобы вместо него принимать 6 GLfloats вместо двух структур, каждый из которых содержит 3 GLfloats, а затем заставил метод init назначить эти GLfloats двум соответствующим структурам переменных экземпляра класса вместо назначения ранее переданных структур непосредственно к структуре переменных экземпляра, все работало нормально без ошибок.
Для уточнения:
У меня была структура, определенная так:
struct xyz{
GLfloat x;
GLfloat y;
GLfloat z;
};
Затем у меня было два ивара пользовательского класса (давайте назовем его foo) с именем position и вращением, оба основанные на этой структуре.
Затем у меня был упрощенный метод инициализации до этого:
-(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 BAD ДОСТУП. Поэтому я изменил метод 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 есть проблемы с использованием структур объектов, но мои структуры состоят только из простых GLfloats, которые не являются объектами ... верно? Я бы лучше знал, почему это работает, прежде чем я продолжу, и, надеюсь, помогу другим людям, испытывающим ту же проблему.
Спасибо,
- Адам Эйсфельд
РЕДАКТИРОВАТЬ: Добавление источника
Попробуй, как мне кажется, я не могу продублировать проблему в тестовом проекте, поэтому в моем коде должно быть что-то не так, как предлагали другие, и я вообще не исправил проблему, только замаскировал ее до позднего времени, что это то, чего я боялся.
Так что если кто-то может взглянуть на этот код, чтобы указать, что здесь может пойти не так (если проблема действительно в методе 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-соединений данного игрока в моем движке. Я написал сценарий для Maya в MEL, который позволяет вам выбрать серию анимированных моделей в области просмотра (в данном случае суставы персонажа-робота, т.е. верхнее и нижнее плечи, голова, туловище и т. Д.), А затем запустить скрипт, который будет проходить через каждую выбранную модель и экспортировать файл .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), затем он содержит список имен. Эти имена относятся как к именам файлов .obj для загрузки в 3D-движок, так и к соответствующим файлам .anim для загрузки загруженных файлов .obj.
Объяснив это, я опишу, как я справляюсь с этим в моем проекте:
Пользователь создает новый класс "AEPlayer", используя указанный выше метод -initWithName: File:
Этот метод сначала открывает файл .dat, экспортированный из maya, чтобы найти имена файлов для загрузки для суставов плеера.
Для каждого имени соединения, найденного в файле .dat, система затем создает новый экземпляр NGLMesh, используя имя соединения с ".obj" в конце, а затем открывает текущий сустав соединения. .anim файл. Система также добавляет инстанцированный NGLMesh в массив суставов игрока.
Для каждого файла .anim. Он считывает файл, используя NSScanner. Он сканирует по 6 строк за раз (положение xyz и поворот xyz каждого кадра). После сканирования данных одного кадра он создает класс AEFrame и устанавливает данные положения кадра в загруженные данные положения, а данные вращения в загруженные данные вращения. Это класс, в котором у меня возникли проблемы с передачей структуры положения xyz и структуры вращения xyz в его параметрах init, но "исправлена", передав вместо этого 6 GLfloats, как показано в коде выше.
Когда создается экземпляр этого кадра, этот кадр добавляется в массив анимаций текущего игрока (NSMutableArray). Этот массив анимаций в конечном итоге становится достаточно большим, поскольку в нем хранятся все данные анимации для каждого соединения в модели. Модель, с которой я тестирую, имеет 42 сустава, каждый сустав имеет 12 кадров анимации, каждый кадр имеет 6 GLfloats.
Когда система завершит загрузку в память всех файлов .obj, найденных в файле .dat, и связанных с ними файлов .obj и .anim, она будет завершена.
В главном цикле выполнения программы я просто перебираю все созданные AEPlayers, и для каждого AEPLayer я перебираю массив соединений игрока, устанавливая положение и вращение NGLMesh, хранящегося у текущего игрока. соединение в любую позицию / поворот, найденную в массиве анимаций игрока для текущего соединения.
Я понимаю, что это длинный путь, чтобы заставить кого-нибудь прочитать все это, но я думал, что я брошу это туда. Кроме того, я знаю, что у меня есть несколько мертвых выделений в приведенном выше фрагменте кода, от которых мне нужно избавиться, но они не способствуют возникновению проблемы (я добавил их после того, как возникла проблема, чтобы увидеть, что происходит с памятью с помощью инструментов) .
Большое спасибо за любую помощь,
- Адам Эйсфельд