I am having an issue I cannot seem to figure out. Done tons of searching, and tried about 50 different variations, so far no dice. Here is my dilemma.
I have 3 methods. One is called when my PageView object loads, another is called whenever a user makes a change, and the last is called whenever the user leaves the page.
First method:
- (void)captureInitialLinesTexture {
@autoreleasepool {
self.initialLinesTextureCaptured = TRUE;
GLubyte *buffer =(GLubyte *) malloc (1024 * 1024 * 4 * sizeof(GLubyte));
glPixelStorei(GL_PACK_ALIGNMENT,1) ;
glReadPixels(0, 0, 1024, 1024, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
glBindTexture(GL_TEXTURE_2D, [pageTexture name]);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1024, 1024, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
NSData __autoreleasing *tempData = [NSData dataWithBytes:buffer length:1024*1024*4*sizeof(GLubyte)];
if (textureData == nil) {
textureData = [self.pageController.notebookDoc newTextureInPage:self.page];
}
[pageTextures addObject:tempData];
if ([pageTextures count] > 5) {
[pageTextures removeObjectAtIndex:0];
}
textureData.textureData = tempData;
textureData.page = self.page;
self.page.texture = textureData;
self.page.lastLineId = [NSNumber numberWithInt:self.pageController.newLineId - 1];
[self.pageController saveNotebookWithUndo:FALSE];
free((GLubyte*)buffer);
}
}
Second method:
-(void)capturePageTexture {
@autoreleasepool {
GLubyte *buffer =(GLubyte *) malloc (1024 * 1024 * 4 * sizeof(GLubyte));
glPixelStorei(GL_PACK_ALIGNMENT,1) ;
glReadPixels(0, 0, 1024, 1024, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
NSData __autoreleasing *tempData = [NSData dataWithBytes:buffer length:1024*1024*4*sizeof(GLubyte)];
[pageTextures addObject:tempData];
if ([pageTextures count] > 5) {
[pageTextures removeObjectAtIndex:0];
}
free((GLubyte*)buffer);
}
}
And last method:
-(void)attemptInstantPageTextureCapture {
if ([pageTextures count] > 0) {
self.page.texture.textureData = [pageTextures lastObject];
self.page.lastLineId = [NSNumber numberWithInt:self.pageController.newLineId - 1];
[self.pageController saveNotebookWithUndo:FALSE];
}
[pageTextures removeAllObjects];
pageTextures = [NSMutableArray arrayWithCapacity:0];
}
My issue is with those NSData *tempData variables. For some reason one of them randomly hangs around after my PageView has gone away (discovered via Allocations Instruments).
If I load the PageView, captureInitialLinesTexture fires. I can then add lots of pen strokes and capturePageTexture fires after every stroke, keeping up to 5 NSData variables in an NSMutableArray names pageTextures. When I leave the PageView, the NSMutableArray is emptied.
Now here is where it gets head-banging-into-wall-ish. The allocation for the NSData is always the same size, 4mb in this case. When the page loads, I have 1 4mb allocation. If I make pen strokes I can get up to 5 more. Then, when I leave the PageView, up to 5 of the 4mb allocations (NSData) get released, but I always get left with 1. If I then go to Call Tree on instruments, it randomly says its from that first method or the second method. Why does it have no problem dumping the 5 NSData that are stored in the array, but then for some reason 1 NSData is left alive somewhere. Furthermore, this mystery NSData comes from either method randomly.
Like I said, I have searched and searched and cannot find a solution to this problem. 4MB of memory is obviously huge, so any leaks of this sort will kill the app over time.
Allocation from one attempt:

Call Tree for that attempt:

Allocation for another attempt:

Call Tree for other attempt:

Same exact code fires every time, and every time it is random which NSData stays alive. Anyone have any idea what I am doing wrong? the “page” and “textureData” variables are NSManagedObjects in case you are wondering. As far as things I have tried:
alloc on the NSData objects then calling initWithBytes:length:
initWithBytesNoCopy:Length: – did not call free((GLubyte*)buffer);
initWithBytesNoCopy:Length:freeWhenDone: – did not call free((GLubyte*)buffer);
NSData class method version of the two above attempts
not using @autoreleasepool
not using __autoreleasing for var declaration
making the NSMutableArray pageTextures a property of PageView
setting PageView to nil before it gets Popped off the NavigationController stack
iterating through the pageTextures mutable array using __strong and setting all the NSData to nil or null.
As mentioned above, this all takes place in a PageView class that is one of many of its kind within a PageViewController that is pushed onto and then popped off of a navigation controller. Once the PageViewController (PageView’s only super view) is popped off the nav stack, one random NSData allocation is still alive.
Any suggestions at all would be extremely appreciated! As far as the core data elements mentioned above, the code works exactly as planned. The only single issue is that sneaky live NSData allocation… 🙁
Solved the problem a while back, but figured I would post what happened in case anyone else has the same issue. Basically the problem was that the NSData objects where being assigned to core data entities, and core data is in control of when it releases its own memory. Thus while the code seemed sound, core data was still holding onto the NSData objects, way after they had gone out of scope and the run loop finished (when ARC would have normally kicked in).
To get around the issue, we just saved the NSData to a file, and changed the model so that the core data entities just contained a string of the file path (NSString) instead of boolean data (NSData).
Problem solved 🙂