NSData内存未释放 - ARC

时间:2012-05-15 01:02:58

标签: objective-c ios memory automatic-ref-counting nsdata

我遇到了一个我似乎无法弄清楚的问题。完成了大量的搜索,尝试了大约50种不同的变化,到目前为止还没有骰子。这是我的困境。

我有3种方法。一个在我的PageView对象加载时被调用,另一个在用户进行更改时被调用,而最后一个在用户离开页面时被调用。

第一种方法:

- (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);
  }
}

第二种方法:

-(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);
  }
}

最后一种方法:

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

我的问题是那些NSData * tempData变量。出于某种原因,在我的PageView消失后(通过Allocations Instruments发现),其中一个随机出现。

如果我加载了PageView,则会触发captureInitialLinesTexture。然后,我可以在每次笔画后添加大量笔划和capturePageTexture,在NSMutableArray名称pageTextures中保留最多5个NSData变量。当我离开PageView时,NSMutableArray被清空。

现在,这就是它被撞到墙上的地方。 NSData的分配总是相同的大小,在这种情况下为4mb。当页面加载时,我有1 4mb的分配。如果我做笔画,我可以多达5个。然后,当我离开PageView时,最多5个4mb分配(NSData)被释放,但我总是留下1.如果我然后去仪器上的Call Tree,它从第一个方法或第二个方法随机地说方法。为什么转储存储在数组中的5 NSData没有问题,但由于某种原因,1 NSData在某处保持活动状态。此外,这个神秘的NSData随机来自任何一种方法。

就像我说的那样,我搜索过并找不到解决这个问题的方法。 4MB的内存显然是巨大的,所以任何这种泄漏都会随着时间推移杀死应用程序。

一次尝试的分配: Allocation from one attempt

该尝试的调用树: Call tree for that attempt

另一次尝试的分配: Allocation for another attempt

调用树进行其他尝试: Call Tree for other attempt

每次都会触发相同的精确代码,并且每次NSData保持活动时都是随机的。任何人都知道我做错了什么?如果您想知道,“page”和“textureData”变量是NSManagedObjects。就我所尝试的事情而言:

对NSData对象进行alloc,然后调用initWithBytes:length:

initWithBytesNoCopy:长度: - 没有调用free((GLubyte *)缓冲区);

initWithBytesNoCopy:Length:freeWhenDone: - 没有调用free((GLubyte *)缓冲区);

上面两次尝试的NSData类方法版本

不使用@autoreleasepool

不使用__autoreleasing进行var声明

使NSMutableArray pageTextures成为PageView

的属性

在弹出NavigationController堆栈之前将PageView设置为nil

使用__strong遍历pageTextures可变数组并将所有NSData设置为nil或null。

如上所述,这一切都发生在一个PageView类中,该类是PageViewController中的一种,它被推入到导航控制器上然后弹出。一旦从导航堆栈中弹出PageViewController(PageView的唯一超级视图),一个随机NSData分配仍然存在。

任何建议都将非常感谢!就上面提到的核心数据元素而言,代码完全按计划工作。唯一的问题是偷偷摸摸的实时NSData分配...... :(

3 个答案:

答案 0 :(得分:4)

一段时间后解决了这个问题,但想到我会发布发生的事情以防其他人有同样的问题。基本上问题是NSData对象分配给核心数据实体,核心数据控制它何时释放自己的内存。因此,虽然代码似乎是合理的,但是核心数据仍然保留在NSData对象之后,在它们超出范围并且运行循环结束后(当ARC通常会被踢入时)。

为了解决这个问题,我们只是将NSData保存到文件中,并更改了模型,以便核心数据实体只包含文件路径(NSString)的字符串而不是布尔数据(NSData)。

问题解决了:))

答案 1 :(得分:1)

malloc在一个线程上分配的内容在另一个线程上释放时不会释放。将您的代码包装在此:

dispatch_async(dispatch_get_main_queue(), ^{
    // malloc and whatever other code goes here...
});

答案 2 :(得分:0)

__autoreleasing关键字没有按照您的想法执行。它用于传递包含初始化它们的方法的未初始化指针的指针。然后,这些方法知道不保留它们初始化的值,因为它的唯一目的是返回在调用方法中使用。在这种情况下你不需要它(默认的强大会很好,当它超出范围时它会被释放)。删除该关键字时,错误是否完全相同?