我有一个需要截图并保存为文件的应用程序。我使用的是ARC,因此不能手动释放变量,而且看来我的代码有严重的泄漏。

这是我正在运行的:

- (BOOL) saveNow:(NSString *)filePath {
    UIImage *image = [self.view getImage];
    NSData *imageData = UIImagePNGRepresentation(image);
    return [imageData writeToFile:filePath atomically:YES];
}

其中getImageUIView上一个类别的方法:
- (UIImage *)getImage {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, [[UIScreen mainScreen]scale]);
    [[self layer] renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *viewImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return viewImage;
}

在非视网膜iPad上运行此代码时,创建UIImage对象将使内存额外增加1 MB,而NSData再添加4 MB,并且由于我多次运行,此内存没有释放!在视网膜iPad上,每次调用saveNow:的费用约为17 MB,这会使设备在运行几次后耗尽内存。

一些额外的信息。我正在循环执行此代码,该循环总共进行了300次以上(每次迭代对视图进行小的更改,并且需要每个屏幕快照以供查看)。如果减少迭代次数以使设备不会耗尽内存,则可以看到一旦包含循环的方法返回就释放了内存。但是,这不是理想的选择,我希望将占用大量内存的代码纳入其自身的功能(saveNow:)应该会有所改善,但事实并非如此。如何强制这些对象在不需要时立即释放,而不是等待父方法返回?希望不必在整个项目上禁用ARC。

编辑:我尝试使用@autoreleasepool这样的:
@autoreleasepool {
    [self saveNow:filePath];
}

结果更好,但不是完美的。块完成后,它将释放大约4 MB的内存,但仍会停留1 MB,直到容器方法返回为止。因此,这是80%的改进(是!),但我的目标是100%:)我将阅读更多有关@autoreleasepool的内容,因为我以前从未使用过它。

最佳答案

我会在@autoreleaspool上发表自己的评论,这是对您有帮助的合法答案。

苹果建议在内存有问题的地方使用@autoreleaspool。以下段落摘自Core Data documentation,但我相信也可以在这种情况下应用:

与许多其他情况一样,当您使用Core Data进行导入时
数据文件,记住可可的“正常规则”很重要
应用开发适用。如果您导入的数据文件
以某种方式进行解析,您可能会创建大量的
临时对象。这些可能会占用大量内存并导致
分页。就像使用非核心数据应用程序一样,您可以
使用本地自动释放池块来限制数量
其他对象驻留在内存中。有关交互的更多信息
在核心数据和内存管理之间,请参阅“减少内存
高架。”

基本上,@ autoreleasepool可以作为编译器在所有临时对象超出范围后释放它们的提示。
您期望内存可以完全释放,Apple框架可能不会这样。幕后可能会有一些缓存(这是公正的想法)。这就是为什么剩余的1MB可能还可以的原因。但是,为了安全起见,我建议增加迭代次数,然后看看会发生什么。

正如您在评论中提到的那样,您的循环很大且嵌套,因此可能还会发生其他情况。尝试摆脱所有额外的操作,看看会发生什么。

希望这会有所帮助,干杯!

关于ios - UIImage和NSData内存泄漏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21099661/

10-11 22:48
查看更多