iOS-UIImage imageWithContentsOfFile 和 imageName 对照(图片加载的内存及优化)

1.官方文档的解释

imageNamed: 把image缓存到内存里面,
此方法在系统缓存中查找具有指定名称的图像对象,并返回最适合主屏幕的图像变体。如果匹配的图像对象尚未在缓存中,此方法将定位并从磁盘或可用资产目录加载图像数据,然后返回结果对象。

系统可以在任何时候清除缓存的图像数据以释放内存。仅对缓存中但当前未使用的图像进行清除。

imageWithContentsOfFile:
或initWithContentsOfFile:方法创建一个图像对象,其中初始数据不在包中。
这些方法每次从磁盘加载图像数据,因此不应该使用它们重复加载相同的图像。

缓存加载方式(imageNamed) :

使用imageNamed这个方法生成的UIImage对象,会在应用的bundle中寻找图片,如果找到则Cache到系统缓存中,作为内存的cache,而程序员是无法操作cache的,只能由系统自动处理,如果我们需要重复加载一张图片,那这无疑是一种很好的方式,因为系统能很快的从内存的cache找到这张图片,但是试想,如果加载很多很大的图片的时候,内存消耗过大的时候,就会会强制释放内存,即会遇到内存警告(memory warnings).由于在iOS系统中释放图片的内存比较麻烦,所以冲易产生内存泄露。

总结下:
何时用imageNamed : 图片资源反复使用到,如按钮背景图片的蓝色背景,这些图片要经常用到,而且占用内存少 button背景图片的蓝色背景。这些图片要常常常使用到,并且占用内存少

不能用 imageNamed : 图片资源较大,加载到内存后,比较耗费内存资源 ,图片一般只使用一次。
(1)图片一般仅仅使用一次。如一些用户的照片资源
(2)图片资源较大,载入到内存后,比較耗费内存资源

非缓存加载方式 (imageWithContentsOfFile) :
相比上面的imageNamed这个方法要写的代码多了几行,使用imageWithContentsOfFile的方式加载的图片,图片会被系统以数据的方式进行加载.返回的对象不会保存在缓存中,一旦对象销毁就会释放内存,所以一般不会因为加载图片的方法遇到内存问题.

imageWithContentsOfFile:比方新手引导界面的图片等等,就适合这样的方式。
imageWithContentsOfFile:使用的封装代码下载

获取MainBundle的图片
+ (UIImage*)imageFromMainBundleFile_zmm:(NSString*)aFileName
{
    NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
    return [UIImage imageWithContentsOfFile:[NSString stringWithFormat:@"%@/%@", bundlePath,aFileName]];
}
获取bundle中的图片(只需传入图片的名字,方法里面自己查找几倍图,然后获得全路径)
/*
1.只需传图片的名字和Bundle的名字
2.找到当前分辨率的图片
3.当前分辨率的图片不存在的话,则查找其它分辨率图片,从高分辨率开始查找


*/
+ (UIImage *)imageFromBundle:(NSString *)aBundle fileName_zmm:(NSString *)aFileName
{
    NSArray *tempArray = [aFileName componentsSeparatedByString:@"."];
    //如果文件不包含后缀,返回空
    if (tempArray.count != 2)
    {
        return nil;
    }
    
    NSString *name = [tempArray objectAtIndex:0];
    NSString *suffix = [tempArray objectAtIndex:1];
    //获取图片时不能直接使用包含@x的图片名称
    if ([name hasSuffix:@"@x"])
    {
        return nil;
    }
    
    //当前屏幕倍数
    NSInteger currentScale = (NSInteger)[[UIScreen mainScreen] scale];
    //获取当前分辨率的图片
    UIImage *image = [self p_getImageFromBundle:aBundle
                                           name:name
                                         suffix:suffix
                                     scale_zmm:currentScale];
    if (image) {
        
        return image;
    }
    
    //如果当前分辨率图片不存在,则查找其它分辨率图片,从高分辨率开始查找
    for (NSInteger tempScale = 3;tempScale >= 1;tempScale--)
    {
        if (tempScale == currentScale)
        {
            continue;
        }
        
        //获取当前分辨率的图片
        UIImage *image = [self p_getImageFromBundle:aBundle
                                               name:name
                                             suffix:suffix
                                         scale_zmm:tempScale];
        if (image) {
            
            return image;
        }
    }
    return nil;
}

#pragma mark - Private Method
+ (NSString *)p_getFileName:(NSString *)aFile
                     suffix:(NSString *)aSuffix
                 scale_zmm:(NSInteger)aScale
{
    NSString *result = @"";
    switch (aScale)
    {
        case 3:
        {
            result = [NSString stringWithFormat:@"%@@%ldx.%@",aFile,(long)aScale,aSuffix];
            break;
        }
        default:
            result = [NSString stringWithFormat:@"%@.%@",aFile,aSuffix];
            break;
    }
    return result;
}

+ (UIImage *)p_getImageFromBundle:(NSString *)aBundleName imageFullName_zmm:(NSString *)aImageFullName
{
    NSString *mainBundlePath = [[NSBundle mainBundle] bundlePath];
    NSString *customBundlePath = [mainBundlePath stringByAppendingPathComponent:aBundleName];
    NSString *filePath = [customBundlePath stringByAppendingPathComponent:aImageFullName];
    UIImage *image = [UIImage imageWithContentsOfFile:filePath];
    return image;
    
}

+ (UIImage *)p_getImageFromBundle:(NSString *)aBundleName
                             name:(NSString *)aName
                           suffix:(NSString *)aSuffix
                       scale_zmm:(NSInteger)aScale
{
    //获取当前分辨率的图片
    NSString *fileFullName = [self p_getFileName:aName suffix:aSuffix scale_zmm:aScale];
    UIImage *image = [self p_getImageFromBundle:aBundleName imageFullName_zmm:fileFullName];
    return image;
}

注意!!!!!!!如果是有很多图片 图片又很大 会出现 "内存不足,内存泄露,甚至是程序的崩溃"
而在iOS系统里面释放图像的内存是一件比較麻烦的事情。有可能会造成内存泄漏。比如:当一 个UIView对象的animationImages是一个装有UIImage对象动态数组NSMutableArray,并进行逐帧动画。当使用imageNamed的方式载入图像到一个动态数组NSMutableArray,这将会非常有可能造成内存泄露。

YYImage 的内存处理

YYImage 的核心就是学习imageWithContentsOfFile:的方法原理去实现imageNamed:方法. 达到imageNamed:方法中没有缓存功能, 最终使得不需要图片的时候即可销毁图片对象.

imageWithContentsOfFile 代替 imageNamed

首先看 YYImage 的代码:

+ (YYImage *)imageNamed:(NSString *)name {

    if (name.length == 0) return nil;

    if ([name hasSuffix:@"/"]) return nil;

    NSString *res = name.stringByDeletingPathExtension;

    NSString *ext = name.pathExtension;

    NSString *path = nil;

    CGFloat scale = 1;

    // If no extension, guess by system supported (same as UIImage).

    NSArray *exts = ext.length > 0 ? @[ext] : @[@"", @"png", @"jpeg", @"jpg", @"gif", @"webp", @"apng"];

    NSArray *scales = [NSBundle preferredScales];

    for (int s = 0; s < scales.count; s++) {

        scale = ((NSNumber *)scales[s]).floatValue;

        NSString *scaledName = [res stringByAppendingNameScale:scale];

        for (NSString *e in exts) {

            path = [[NSBundle mainBundle] pathForResource:scaledName ofType:e];

            if (path) break;

        }

        if (path) break;

    }

    if (path.length == 0) return nil;

    NSData *data = [NSData dataWithContentsOfFile:path];

    if (data.length == 0) return nil;

    return [[self alloc] initWithData:data scale:scale];

}

 

从代码可以看出 [YYImage imageNamed:]这个方法底层是利用通过一定的计算获取到最佳尺寸, 然后枚举图片匹配图片文件名, 拼接成路径后利用NSData创建出UIImage. 本质上和imageWithContentsOfFile:没有啥区别.


imageWithContentsOfFile 的缺点
当我们需要图片的时候就会去沙盒中读取这个图片文件, 转换成UIImage对象来使用. 现在假设一种场景:
image@2x.png 图片占用 5kb 的内存
image@2x.png 在多个界面都用到, 且有7处会同时显示这个图片
通过代码分析就可以知道 Resource 这个方式在这个情景下会占用 5kb/个 X 7个 = 35kb 内存. 然而, 在 ImageAssets 方式下, 全部取自字典缓存中的UIImage, 无论有几处显示图片, 都只会占用 5kb/个 X 1个 = 5kb 内存. 此时 Resource 占用内存将会更大.
由于 YYImage 的核心就是利用imageWithContentsOfFile:代替imageNamed:, 所以这也是 YYImage 的缺陷之处

imageName 的缺点
第一次读取的图片保存到缓冲区, 然后永不销毁. 如果这个图片过大, 占用几百 kb, 这一块的内存将不会释放, 必然导致内存的浪费, 而且这个浪费的周期与APP的生命周期同步.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值