美文网首页IOS文件ios开发问题记录iOS Developer
UIImagePNGRepresentation 保存图片时Cr

UIImagePNGRepresentation 保存图片时Cr

作者: _Thinking_ | 来源:发表于2016-08-31 19:23 被阅读2966次

    最近遇到一个保存图片时的崩溃, 崩溃信息如下:

    0  CoreFoundation!__exceptionPreprocess + 0x84
    1  libobjc.A.dylib!objc_exception_throw + 0x38
    2  CoreFoundation!+[NSException raise:format:] + 0x7c
    3  Foundation!_NSMutableDataGrowBytes + 0x300
    4  Foundation!-[NSConcreteMutableData appendBytes:length:] + 0x174
    5  ImageIO!CGImageWriteSessionPutBytes + 0x94
    6  ImageIO!write_fn + 0x28
    7  ImageIO!png_write_chunk_data + 0x34
    8  ImageIO!_cg_png_write_complete_chunk + 0x40
    9  ImageIO!png_compress_IDAT + 0x160
    10 ImageIO!png_write_find_filter + 0xa80
    11 ImageIO!_cg_png_write_row + 0x300
    12 ImageIO!writeOnePng + 0x1358
    13 ImageIO!_CGImagePluginWritePNG + 0x90
    14 ImageIO!CGImageDestinationFinalize + 0x1e8
    15 UIKit!UIImagePNGRepresentation + 0x248
    16 MFAFImageCache addImage:forRequest:withAdditionalIdentifier:
    
    

    提示的异常信息是:

    Exception Name: NSMallocException
    Exception Reason: *** -[NSConcreteMutableData appendBytes:length:]: unable to allocate memory for length (1751849)
    

    查看一下调用函数, 主要调用如下:

    NSString *filePath = [FileModel getLocalFileName:kPhotosDirectory byUrl:[[request URL] absoluteString]];
     if (image) {
         @try {
             NSData *imageData = UIImagePNGRepresentation(image);
             [imageData writeToFile:filePath atomically:YES];
         } @catch (NSException *exception) {
             MFLogError(@"ImageCache", @"save image error:%@", exception);
         } @finally {
             
         }
     }
    
    

    说明在执行 NSData *imageData = UIImagePNGRepresentation(image) 这个操作时,崩溃了。而且崩溃的原因是内存分配出错。 通常这种问题都是因为内存不足引起的。但是查看了一下log, 发现并没有 MemoryWarning 这句日志,说明程序还没来得及处理系统的内存警告就崩溃了

    根据这些现象猜测可能的原因

    • 图片比较大
    • 外层也许有个for循环,多次调用,导致临时对象没来得及马上释放

    针对这两种猜测,作了相应的处理

    图片内容较大

    其实有 UIImage 对象的时候,说明图片已经可以正常展示,加载这张图片到内存暂时是没有问题的。但保存的时候又根据 UIImage 对象生成 NSData, 然后在通过NSData保存文件。 在NSData写入文件到释放前,内存里面其实有两份 图片 的数据, 一份是显示的, 一份是准备用来保存的NSData。 这样其实就是临时分配了一块大空间,如果可以减少临时对象的分配,直接读取UIImage的数据来存储就可以降低内存的使用。
    通过查阅文档,发现Image IO 的库可以做到。

    于是,保存函数改成这样:

    + (BOOL)saveImage:(UIImage *)image toFile:(NSString *)filePath
    {
        if (!image.CGImage) {
            return NO;
        }
        
        CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath];
        CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, nil);
        if (!destination) {
            return NO;
        }
        
        CGImageDestinationAddImage(destination, image.CGImage, nil);
        CGImageDestinationFinalize(destination);
        CFRelease(destination);
        
        return YES;
    }
    
    

    临时对象没有马上释放

    这种情况比较好处理,就是因为加到 AutoReleasePool 的对象没有马上释放,必须要等下一次 RunLoop 循环才会清理。
    这种问题可以加 @autoreleasepool{} 处理。
    最后函数变成:

    + (BOOL)saveImage:(UIImage *)image toFile:(NSString *)filePath
    {
        if (!image.CGImage) {
            return NO;
        }
        
        @autoreleasepool {
            CFURLRef url = (__bridge CFURLRef)[NSURL fileURLWithPath:filePath];
            CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, nil);
            if (!destination) {
                return NO;
            }
            
            CGImageDestinationAddImage(destination, image.CGImage, nil);
            CGImageDestinationFinalize(destination);
            CFRelease(destination);
        }
        
        return YES;
    }
    
    

    当然 @autoreleasepool 写在这里作用并不大, 应该要加在相应的for()循环里面。
    因此定义一个宏,方便替换,只要把原来的 for 替换成 AutoRelease_for 就可以。

    #define AutoRelease_for(...) for(__VA_ARGS__) @autoreleasepool 
    

    相关文章

      网友评论

      • NingL:您好,求教 [NSDictionary writeToFile: atomically: ]这种由于内存分配过大导致的崩溃该如何处理呢?崩溃原因跟您上面所述类似也是由于内存分配过大导致,不胜感激!
      • babystyles:CGImageDestinationRef destination = CGImageDestinationCreateWithURL(url, kUTTypePNG, 1, nil); 这句话中 kUTTypePNG 是指什么,是要导入什么文件吗,我这边会报错
      • 林神_iOS:你好,请问,最后那个“for 替换成 AutoRelease_for” 是什么意思?
        _Thinking_:@林神_iOS 就是for 完后马上清理一下临时对象

      本文标题:UIImagePNGRepresentation 保存图片时Cr

      本文链接:https://www.haomeiwen.com/subject/jnqcettx.html