我有一个代码迭代器,它是一个测试函数,用于将原始数据转换为UIImage对象以进行文件存储。我有两个失败路径或选项。我可以失败:
A.)通过泄漏(仪器没有显示它 - 仪器所有分配似乎都是稳定的,我必须多次迭代50次,它会通过仪器或正常情况运行测试功能) 。由于下面的“b”选项,记忆似乎是罪魁祸首。
B.)我可以在UIImage中强制释放CG对象,在这种情况下应用程序将继续。一旦迭代器完成,测试将在最后一次迭代时崩溃,试图释放从我的子函数返回的图像下面的CGImage。无论我请求多少次迭代,迭代器都会完成它的运行。这将指出我已修复泄漏的事实。那么现在我想把最后一次迭代的特殊内容拼凑起来? (ARC?)
- (UIImage *) convertRGBToUIImage:(unsigned char *) buffer
withWidth:(int) width
withHeight:(int) height
{
CGColorSpaceRef colorSpace = nil;
CGContextRef context = nil;
CGImageRef ref = nil;
UIImage *retImg = nil;
colorSpace = CGColorSpaceCreateDeviceRGB();
if (colorSpace == NULL)
{
NSLog(@"ERROR: allocating color space convert Bitmap\n");
return nil;
}
context = CGBitmapContextCreate (buffer, width, height, 8, width * 4, colorSpace, kCGImageAlphaPremultipliedLast);
CGColorSpaceRelease(colorSpace );
if (context == NULL)
{
NSLog(@"ERROR: Context not created!");
return nil;
}
ref = CGBitmapContextCreateImage(context);
CGContextRelease(context);
retImg = [UIImage imageWithCGImage:ref];
CGImageRelease(ref);
return retImg;
}
迭代器基础知识
for(int i = 0; i < numberiterations; i++)
{
UIImage *completedImage = [phelper convertRGBToUIImage:pBuffer withWidth:width withHeight:height]
//...save it out or display it
//Turn on one of the scenarios below
//scenario "A" -- will leak memory (instruments doesn't seem to turn anything up)
//ARC free
//completedImage = nil; //this will leak and fail after ~50 iterations
//-or-
//scenario "B" -- will crash on the very last iteration every time but can run x times
//CGImageRelease([completedImage CGImage];
}
Xcode中的分析工具更喜欢上面的场景“A”。我可以做到这一点,一切看起来都很棒,但它不会成功完成测试。我认为这指向ARC。我试图找出__bridge类型的转换没有成功。我似乎无法正确使用语法,也许它不是我的问题的答案。任何提示或想法都将非常感激。
答案 0 :(得分:3)
仪器没有出现泄漏的原因是 - 技术上 - 没有
你所看到的是来自@autoreleasepool
的记忆压力不够紧张:
您的方法convertRGBToUIImage:withWidth:withHeight:
会返回一个新的自动释放图像,但最接近的@autoreleasepool
位于您的循环之外。
这意味着在你的循环所在的方法返回之前,所有这些图像都不会被处理掉。
这就是为什么你看到你的内存消耗在方案A中每次迭代都在增长而你的应用程序在方案B中的循环之后崩溃的原因:
UIImage
- 主要是 - 围绕CGImageRef
的Objective-C包装器,以便方案B中的额外CGImageRelease
从UIImage
下方拉出后备存储脚。这个后备存储恰好是UIImage
内存饥渴的部分,所以内存消耗的急剧增加几乎消失了。
在返回循环的方法后,当@autoreleasepool
耗尽时,它会处理所有临时UIImage
个实例 - 而这些实例又要处置它们的后备 - CGImageRef
...和热潮会有节目。
诀窍是在循环中创建自己的@autoreleasepool
:
for (int i = 0; i < numberiterations; i++) {
@autoreleasepool {
// your old iteration body comes here
}
}
现在,方案A将按预期运行,方案B将在第一次迭代结束时立即崩溃 - 这也是预期的。
如果您在“发布”方案下在ARC下运行原始代码,(即编译器标志-Os
又称“最快的最小”),方案A很可能不会崩溃!
这种看似奇怪的行为的原因是,ARC使用一组执行实际内存管理的特殊C函数来装饰您的代码 - 并且因为这些函数是常量的(Objective-C中的方法调用不一定)编译器可以优化它们的某些组合。
这导致UIImage
永远不会被插入自动释放池中,因此在循环的每次迭代后被处理掉。