Ios 为什么CIContext.createCGImage会导致内存泄漏?

Ios 为什么CIContext.createCGImage会导致内存泄漏?,ios,memory-leaks,opengl-es,ios9,core-image,Ios,Memory Leaks,Opengl Es,Ios9,Core Image,我只在iOS 9上观察到这种行为;iOS 8工作正常 我怀疑这可能是SDK上的一个bug,我已经打开了苹果(22644754)的雷达,但我发现它太奇怪了,以至于我觉得我可能错过了一个电话或一个避免泄漏的步骤 我观察到,每次调用CIContext.createCGImage时,内存使用量都会增加。棘手的是,内存增加发生在应用程序之外 如果您查看Xcode中的“内存报告”,可以在“其他进程”部分看到内存的增加 基本上,造成问题的原因如下(我已将代码简化为重现泄漏所需的部分): 首先,我创建了一个由E

我只在iOS 9上观察到这种行为;iOS 8工作正常

我怀疑这可能是SDK上的一个bug,我已经打开了苹果(22644754)的雷达,但我发现它太奇怪了,以至于我觉得我可能错过了一个电话或一个避免泄漏的步骤

我观察到,每次调用CIContext.createCGImage时,内存使用量都会增加。棘手的是,内存增加发生在应用程序之外

如果您查看Xcode中的“内存报告”,可以在“其他进程”部分看到内存的增加

基本上,造成问题的原因如下(我已将代码简化为重现泄漏所需的部分):

首先,我创建了一个由EAGLContext支持的CIContext:

let glContext = EAGLContext(API: .OpenGLES2)!
let ciContext = CIContext(EAGLContext: glContext, options: [kCIContextOutputColorSpace : NSNull()])
然后,我使用以下方法渲染图像:

let input = CIImage(image: UIImage(named: "DummyImage")!)!
ciContext.createCGImage(input, fromRect: input.extent)
DummyImage只是一个示例图像文件。泄漏与此图像的大小直接相关,因此最好使用较大的泄漏,以使问题更加明显

如您所见,我没有使用任何CIFILTER(使用它们会导致相同的结果),也没有捕获结果图像(即使捕获了它,也无法使用CGImageRelease,因为对象是自动管理的)

如果渲染代码执行的次数足够多,内存将增长太多,以至于运行的应用程序将被杀死

一个有趣的观察结果是,销毁CIContext没有什么区别,但销毁EagleContext确实会返回所获取的内存。这让我认为泄漏发生在OpenGL方面

我的代码中是否缺少任何可能导致泄漏的内容?我能打个电话来释放EagleContext占用的内存吗?(始终重新创建它不是一个选项,因为这是一个成本高昂的操作)


我创建了一个简单的项目来重现这个问题。您可以在以下网址找到:

复制的步骤是:

  • 在设备上打开并运行连接的项目
  • 观察Xcode上的“内存报告”屏幕。增长将出现在“使用情况比较”饼图的“其他流程”部分
  • 该应用程序提供三个按钮。它们中的每一个都将执行createCGImage命令一定次数(显示在按钮标签上)
  • 点击任何一个按钮都会导致“其他进程”的内存使用量增加。在执行多个createCGImage调用后,这可能会更加明显
  • 点击100个渲染按钮将更清楚地显示效果
  • 当内存增长过快时,应用程序将崩溃

  • 我解决这个问题的方法是将上下文呈现到缓冲区中,然后将其作为JPG写入文件。我必须进一步了解如何在旧一代iOS设备上优化此流程,但与createCGImage相比,它似乎工作得很好。此代码对于将CIImage转换为JPEG或位图数据也很有用。完整的示例代码可以在这里看到:


    这已被确认为iOS 9上的一个bug。该问题已在iOS 9.1 beta 3上解决


    原始问题中的代码是正确的。在iOS 9之前的版本或从iOS 9.1 Beta 3开始的版本上,不需要进行任何修改来防止泄漏。

    为什么
    bytesperpoixel=8
    ?kciformatrba8不是每像素4字节的格式吗?
    static CIContext *ctx = nil;
    
    if (!ctx)
    {
        ctx = [CIContext contextWithOptions:@{kCIContextWorkingColorSpace:[NSNull null]}];
    }
    
    CGSize bounds = enhancedImage.extent.size;
    int bytesPerPixel = 8;
    uint rowBytes = bytesPerPixel * bounds.width;
    uint totalBytes = rowBytes * bounds.height;
    uint8_t *byteBuffer = malloc(totalBytes);
    
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    
    [ctx render:enhancedImage toBitmap:byteBuffer rowBytes:rowBytes bounds:enhancedImage.extent format:kCIFormatRGBA8 colorSpace:colorSpace];
    
    CGContextRef bitmapContext = CGBitmapContextCreate(byteBuffer,bounds.width,bounds.height,bytesPerPixel,rowBytes,colorSpace,kCGImageAlphaNoneSkipLast);
    
    CGImageRef imgRef = CGBitmapContextCreateImage(bitmapContext);
    
    CGColorSpaceRelease(colorSpace);
    CGContextRelease(bitmapContext);
    free(byteBuffer);
    
    if (imgRef == NULL) { goto release; }
    
    
    CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath];
    CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypeJPEG, 1, NULL);
    CGImageDestinationAddImage(destination, imgRef, nil);
    CGImageDestinationFinalize(destination);
    CFRelease(destination);
    
    success = YES;
    
    goto release;
    
    release :
    {
        CFRelease(imgRef);
    
        if (success)
        {
            //completionHandler(filePath);
        }
    
        dispatch_resume(_captureQueue);
    }