Question

I'm having a memory leak in my iOS app when allocating many images to a single variable.

A simplified version of my update function, but still demonstrates the point.

This is being called 10 times a second. I need the Allocation to free after it renders but I'm not sure how to do it.

- (void) update
{
PSSprite * score1sDigit = [[PSSprite alloc] initWithFile:[NSString stringWithFormat:@"#%i.png", score%10] effect:self.effect];
}

I'm using this class to allocate memory for the for the texture and to render it

#import <Foundation/Foundation.h>
#import <GLKit/GLKit.h>

@interface PSSprite : NSObject

@property (assign) GLKVector2 position;
@property (assign) CGSize contentSize;
@property (assign) GLKVector2 moveVelocity;

- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect;
- (void)render;
- (void)update:(float)dt;

@end

And

#import "PSSprite.h"

typedef struct {
    CGPoint geometryVertex;
    CGPoint textureVertex;
} TexturedVertex;

typedef struct {
    TexturedVertex bl;
    TexturedVertex br;    
    TexturedVertex tl;
    TexturedVertex tr;    
} TexturedQuad;

@interface PSSprite()

@property (strong) GLKBaseEffect * effect;
@property (assign) TexturedQuad quad;
@property (strong) GLKTextureInfo * textureInfo;

@end

@implementation PSSprite
@synthesize effect = _effect;
@synthesize quad = _quad;
@synthesize textureInfo = _textureInfo;
@synthesize position = _position;
@synthesize contentSize = _contentSize;
@synthesize moveVelocity = _moveVelocity;

- (id)initWithFile:(NSString *)fileName effect:(GLKBaseEffect *)effect {
    if ((self = [super init])) {  
        // 1
        self.effect = effect;

        // 2
        NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithBool:YES],
                              GLKTextureLoaderOriginBottomLeft, 
                              nil];

        // 3
        NSError * error;
        NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];
        // 4
        self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:path options:options error:&error];
        if (self.textureInfo == nil) {
            NSLog(@"Error loading file: %@", [error localizedDescription]);
            return nil;
        }
        self.contentSize = CGSizeMake(self.textureInfo.width, self.textureInfo.height);
        //Set up Textured Quad
        TexturedQuad newQuad;
        newQuad.bl.geometryVertex = CGPointMake(0, 0);
        newQuad.br.geometryVertex = CGPointMake(self.textureInfo.width, 0);
        newQuad.tl.geometryVertex = CGPointMake(0, self.textureInfo.height);
        newQuad.tr.geometryVertex = CGPointMake(self.textureInfo.width, self.textureInfo.height);

        newQuad.bl.textureVertex = CGPointMake(0, 0);
        newQuad.br.textureVertex = CGPointMake(1, 0);
        newQuad.tl.textureVertex = CGPointMake(0, 1);
        newQuad.tr.textureVertex = CGPointMake(1, 1);
        self.quad = newQuad;

    }
    return self;
}



- (GLKMatrix4) modelMatrix {

    GLKMatrix4 modelMatrix = GLKMatrix4Identity;    
    modelMatrix = GLKMatrix4Translate(modelMatrix, self.position.x, self.position.y, 0);
    modelMatrix = GLKMatrix4Translate(modelMatrix, -self.contentSize.width/2, -self.contentSize.height/2, 0);
    return modelMatrix;

}

- (void)render { 

    // 1
    self.effect.texture2d0.name = self.textureInfo.name;
    self.effect.texture2d0.enabled = YES;

    self.effect.transform.modelviewMatrix = self.modelMatrix;

    // 2    
    [self.effect prepareToDraw];

    // 3
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);

    // 4
    long offset = (long)&_quad;        
    glVertexAttribPointer(GLKVertexAttribPosition, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, geometryVertex)));
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(TexturedVertex), (void *) (offset + offsetof(TexturedVertex, textureVertex)));

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}

- (void)update:(float)dt {
    GLKVector2 curMove = GLKVector2MultiplyScalar(self.moveVelocity, dt);
    self.position = GLKVector2Add(self.position, curMove);    
}

@end

I'm not allowed to Dealloc or release anything Because my program is using ARC to automatically handle the memory.

After debugging and profiling, it shows I have thousands of mallocs with a reference count of +1, coming from: libGFXShared.dylib. The responsible caller is gfxAllocateTextureLevel

Was it helpful?

Solution

Found the answer here: Release textures (GLKTextureInfo objects) allocated by GLKTextureLoader

Apparently passing the texture to OpenGL puts OpenGL in charge of the memory forcing you to use glDeleteTextures to delete it.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top