ios - UIImage setImage 崩溃

标签 ios objective-c memory-leaks ffmpeg uiimage

我用 ffmpeg 实现了一个视频播放器。每一帧都解码成功,可以保存为有效的jpg文件,在模拟器中运行时可以显示在UIImageView中。但是,当我在模拟器中运行我的应用程序时,内存会无限增长。此外,应用程序在设备上运行时执行 p_diaplayNextFrame 2 次后会崩溃。如果我评论 self.imageView.image = frame;,内存不会泄漏,应用程序不会在模拟器或设备上崩溃。

-(void)p_displayNextFrame
{
    ZCVFrameSec *frameSec = [video getNextVideoFrameSec];
    UIImage *frame = [frameSec toUIImage];
    static int fi = 0;

    NSAssert( [NSThread isMainThread], @"Fatal error: must be main thread" );
    NSString *fileName = [Utilities documentsPath:[NSString stringWithFormat:@"image%06d.jpg",fi++]];
    NSLog(@"p_displayNextFrame write image file: %@",fileName);

    // frame is saved successfully as jpg, I can view it
    [UIImageJPEGRepresentation(frame, 0.7) writeToFile:fileName atomically:YES];

    // leak(but not crash) in emulator, crash on device
    self.imageView.image = frame;

    double delayInSeconds = 0.05;//1/30.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self p_displayNextFrame];
    });
}

ZCVFrameSec 的toUIImage

- (UIImage*) toUIImage
{
    CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
    CFDataRef data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, [self.data bytes], self.width * self.height * 3,kCFAllocatorNull);
    NSAssert( [self.data length] == self.width * self.height * 3,
             @"Fatal error: data length:%d, width:%d, height:%d, mul3=%d",
             [self.data length],
             self.width, self.height, self.width * self.height * 3 );

    CGDataProviderRef provider = CGDataProviderCreateWithCFData(data);
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGImageRef cgImage = CGImageCreate(self.width,
                                       self.height,
                                       8,
                                       24,
                                       3 * self.width,
                                       colorSpace,
                                       bitmapInfo,
                                       provider,
                                       NULL,
                                       NO,
                                       kCGRenderingIntentDefault);
    UIImage *image = [UIImage imageWithCGImage:cgImage];
    CGImageRelease(cgImage);
    CGColorSpaceRelease(colorSpace);
    CGDataProviderRelease(provider);
    CFRelease(data);

    return image;
}

崩溃信息:

vImage`__vConvert_RGB888toBGRA8888_block_invoke98:
0x2d72aeb0:  push   {r4, r5, r6, r7, lr}
0x2d72aeb2:  add    r7, sp, #0xc
0x2d72aeb4:  push.w {r8, r10}
0x2d72aeb8:  ldr    r2, [r0, #0x14]
0x2d72aeba:  ldr    r2, [r2, #0x4]
0x2d72aebc:  ldr    r2, [r2, #0x10]
0x2d72aebe:  cmp    r2, #0x0
0x2d72aec0:  beq.w  0x2d72b076                ; __vConvert_RGB888toBGRA8888_block_invoke98 + 454
0x2d72aec4:  vldr   d16, [pc, #440]
0x2d72aec8:  vmov.i32 q10, #0xff000000
0x2d72aecc:  lsl.w  r9, r1, #0x3
0x2d72aed0:  vldr   d18, [pc, #436]
0x2d72aed4:  mov.w  r12, #0x0
0x2d72aed8:  ldr.w  r8, [r0, #24]
0x2d72aedc:  add.w  r5, r0, #0x1c
0x2d72aee0:  add.w  r6, r12, r9
0x2d72aee4:  ldm    r5, {r2, r4, r5}
0x2d72aee6:  ldr    r3, [r0, #0x28]
0x2d72aee8:  ldr    r1, [r0, #0x2c]
0x2d72aeea:  mla    r2, r2, r6, r8
0x2d72aeee:  mla    lr, r5, r6, r4
0x2d72aef2:  mla    r3, r1, r6, r3
0x2d72aef6:  tst.w  r3, #0xf
0x2d72aefa:  bne    0x2d72af44                ; __vConvert_RGB888toBGRA8888_block_invoke98 + 148
0x2d72aefc:  tst.w  r2, #0xf
0x2d72af00:  bne    0x2d72af80                ; __vConvert_RGB888toBGRA8888_block_invoke98 + 208
0x2d72af02:  ldr    r4, [r0, #0x30]
0x2d72af04:  movs   r1, #0x0
0x2d72af06:  tst.w  lr, #0xf
0x2d72af0a:  bne    0x2d72afbc                ; __vConvert_RGB888toBGRA8888_block_invoke98 + 268
0x2d72af0c:  cmp    r4, #0x10
0x2d72af0e:  blo    0x2d72aff2                ; __vConvert_RGB888toBGRA8888_block_invoke98 + 322
0x2d72af10:  ldr    r4, [r0, #0x34]
0x2d72af12:  vld3.8 {d0, d2, d4}, [r2]!
Thread 1: EXC_BAD_ACCESS (code=1,address=0x7403000)

感谢任何提示!!!

最佳答案

我认为问题是因为 ZCVFrameSec *frameSec 在超出 -(void)p_displayNextFrame 方法的范围后被释放。

如果您在 frameSec 中的字节数组上使用了 CFDataCreateWithBytesNoCopy,那么字节数组可能会在您使用完 frameSec 之前消失cgImage,或者您可以说,在您的 UIImage *frame 中。这导致了 EXC_BAD_ACCESS 异常。

这就是为什么更改为 CFDataCreate 而不是 CFDataCreateWithBytesNoCopy 的原因。


要回答您的下一个问题,如何避免复制框架,我认为有很多方法可以做到这一点。您可以一直按住 frameSec,直到将 imageView 更改为下一帧,然后您可以释放

为此,您只能声明一个属性来保存当前帧。像这样:

-(void)p_displayNextFrame
{
    ZCVFrameSec *frameSec = [video getNextVideoFrameSec];
    UIImage *frame = [frameSec toUIImage];
    static int fi = 0;

    NSAssert( [NSThread isMainThread], @"Fatal error: must be main thread" );
    NSString *fileName = [Utilities documentsPath:[NSString stringWithFormat:@"image%06d.jpg",fi++]];
    NSLog(@"p_displayNextFrame write image file: %@",fileName);

    // frame is saved successfully as jpg, I can view it
    [UIImageJPEGRepresentation(frame, 0.7) writeToFile:fileName atomically:YES];

    // leak(but not crash) in emulator, crash on device
    self.imageView.image = frame;

    // At your header you should declare : @property ZCVFrameSec *currentFrameSec;
    self.currentFrameSec = frameSec;

    double delayInSeconds = 0.05;//1/30.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
        [self p_displayNextFrame];
    });
}

关于ios - UIImage setImage 崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24179937/

相关文章:

ios - 如何截取 UIView 的屏幕截图并使用它具有模糊背景?

ios - 不使用 UIDocument 获取 iCloud URL 的数据内容

ios - 使一个属性强,非原子的objective-c

tomcat - Tomcat 6 中的 sun.net.www.http.HttpClient 内存泄漏

python - ThreadPoolExecutor 中的 ''.join() 占用内存

ios - iPad 应用程序在锁定屏幕后变为非事件状态

ios - 如何在 UINavigationBar 下方添加 UISegmentControl?

ios - Swift 4 : Pinch gesture, 图像尺寸反弹

ios - Xcode 6 版本不支持 iPhone 5s iOS

c++ - 如何正确处理垃圾收集并避免使用自定义对象指针集合的内存泄漏...?