我看到一个巨大的内存泄漏时,通过渲染一个不可见的视图到上下文创建图像。我已经将它简化为最基本的实现,并确定了导致内存泄漏的两行代码:renderInContext
和UIImagePNGRepresentation
。如果我将两者都评论出来,则不会发生泄漏,但如果其中一个未注释,则会发生泄漏,如果两者均未注释,则会发生两处泄漏。 每次调用下面的方法时,内存使用量会显着增加(如预期的那样),然后一段时间后会下降,但比调用前高出约0.8 MB。内存泄漏renderInContext和UIImagePNGRepresentation
如何解决这个问题以确保没有内存泄漏?
public class func imageDataForSymbol(symbol: String) -> NSData? {
var imageData: NSData!
let dimension = 180
let label = UILabel(frame: CGRectMake(0, 0, CGFloat(dimension), CGFloat(dimension)))
label.text = symbol
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGImageAlphaInfo.PremultipliedLast.rawValue
let bitmapContext = CGBitmapContextCreate(nil, dimension, dimension, 8, 0, colorSpace, bitmapInfo)!
label.layer.renderInContext(bitmapContext) //FIXME: causing leak!!
let cgImage = CGBitmapContextCreateImage(bitmapContext)!
let image = UIImage(CGImage: cgImage)
imageData = UIImagePNGRepresentation(image)! //FIXME: causing leak!!
return imageData
}
为了测试它,在viewDidAppear
:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
dispatch_async(dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), ^{
NSData *d = [ImageGenerator imageDataForSymbol:@"W"];
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"triggered");
});
});
});
如果有更好的方式来为UILabel
的layer
的图像创建NSData
,我完全赞成。我想不出有什么不同的方式来获得它,除了创建CIImage
从CGImage
然后从CIImage
到UIImage
然后从UIImage
到NSData
。请注意,它不需要很快,但它确实需要在后台线程上创建图像,以确保UI保持对其他输入的响应。
@马特使用模拟器,将尝试在iPad发布模式了一下,报到!这并不像看起来那么古怪,除了单一标签之外,我正在做更多奇特的事情,但这是修剪下来的代码,尽管它表现出了泄漏。我只会像往常一样渲染它,但在后台线程上搞UIKit是一个很大的禁忌,我需要应用程序在发生这种情况时继续响应。 – Joey
@matt'UIGraphicsBeginImageContext'方法是我最初采用的方法,但不应该在后台线程上运行,并且我无法在主线程上运行它 - 请参阅下面链接的问题。我现在采用的方法是在答案中显示的方法:使用Core Graphics代替。 http://stackoverflow.com/questions/12843777/renderincontext-memory-leak-if-not-use-on-main-thread/ – Joey
在后台线程的UIGraphics上下文中呈现标签不起作用...文本没有出现。 http://stackoverflow.com/questions/30512053/swift-uilabel-text-not-renderer-when-using-renderincontext-asynchronously – Joey