iOS开发内存优化之SDWebImage内存占用过高
SDWebImage 内存缓存原理:
1.从Cache内存取
2.从磁盘取(因为有解码过程)(内存过大因为解码太高分辨率和图片过大)
3.下载()图片太大 也会引起内存问题
解码问题解决
在SDImageCache.m中找到diskImageForKey方法,发现SDWebImage每次加载网络图片都会进行解码,并将图片存到cache使得之后的加载更加快,效果更加好。但是问题就在于去解码这个操作,如果传进的图片分辨率特别的高,它的解码会消耗大量的内存,所以当图片过大时不进行解码操作。
- (nullable UIImage *)diskImageForKey:(nullable NSString *)key {
NSData *data = [self diskImageDataBySearchingAllPathsForKey:key];
if (data) {
UIImage *image = [UIImage sd_imageWithData:data];
image = [self scaledImageForKey:key image:image];
//当图片过大时(大于5M)不解码
if (data.length > 5*1024*1024) {
return image;
}else{
if (self.config.shouldDecompressImages) {
image = [UIImage decodedImageWithImage:image];
}
return image;
}
}
else {
return nil;
}
}
一般我们都是使用:
+ (nullable UIImage *)imageNamed:(NSString *)name; // load from main bundle
+ (nullable UIImage *)imageWithContentsOfFile:(NSString *)path;
两种方式加载图片
但是为什么要对图片进行解码?
难道不能直接使用上面的两种加载方式直接进行加载显示吗,答案是可以,而且大概我们编码都是使用上面的两种方式直接在主线程加载图片然后显示UIImageView上,并且并没有发现什么问题。那为什么SDWebImage 还要费劲去进行解码图片呢,其实我们自己不解码图片我们也是可以直接使用的(其实是系统为我们进行了解码的操作),一般下载的图片或者我们手动拖进主bundle 的图片都是PNG 或者JPG 其他格式的图片,这些图片都是经过编码压缩后的图片数据,并不是控件可以直接显示的位图,如果我们直接使用 "+ (nullable UIImage *)imageNamed:(NSString *)name" 来加载图片,系统默认会在主线程立即进行图片的解码工作,这个过程就是把图片数据解码成可供控件直接显示的位图数据,由于这个解码操作比较耗时,并且默认是在主线程进行,所以当在主线程调用了大量的 "+ (nullable UIImage *)imageNamed:(NSString *)name" 方法后就会产生卡顿。(同时由于位图体积较大,所以在磁盘缓存中不会直接缓存位图数据,而是编码压缩过的PNG 或者JPG 数据)
对于太大的图片解决方案
对SDWebImage进行修改,在"UIImage+MultiFormat.m"文件中
+ (UIImage *)sd_imageWithData:(NSData *)data {
if (!data) {
return nil;
}
UIImage *image;
NSString *imageContentType = [NSData sd_contentTypeForImageData:data];
if ([imageContentType isEqualToString:@"image/gif"]) {
image = [UIImage sd_animatedGIFWithData:data];
}
#ifdef SD_WEBP
else if ([imageContentType isEqualToString:@"image/webp"])
{
image = [UIImage sd_imageWithWebPData:data];
}
#endif
else {
image = [[UIImage alloc] initWithData:data];
UIImageOrientation orientation = [self sd_imageOrientationFromImageData:data];
if (orientation != UIImageOrientationUp) {
image = [UIImage imageWithCGImage:image.CGImage
scale:image.scale
orientation:orientation];
}
}
return image;
}
image = [[UIImage alloc] initWithData:data];
图片取出来的时候就已经巨大无比,占用了很大的内存。
在该文件中添加图片压缩方法(循环压缩方法):
+(UIImage *)compressImageWith:(UIImage *)image
{
float imageWidth = image.size.width;
float imageHeight = image.size.height;
float width = 640;
float height = image.size.height/(image.size.width/width);
float widthScale = imageWidth /width;
float heightScale = imageHeight /height;
// 创建一个bitmap的context
// 并把它设置成为当前正在使用的context
UIGraphicsBeginImageContext(CGSizeMake(width, height));
if (widthScale > heightScale) {
[image drawInRect:CGRectMake(0, 0, imageWidth /heightScale , height)];
}
else {
[image drawInRect:CGRectMake(0, 0, width , imageHeight /widthScale)];
}
// 从当前context中创建一个改变大小后的图片
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
// 使当前的context出堆栈
UIGraphicsEndImageContext();
return newImage;
}
(UIImage *)sd_imageWithData:(NSData *)data方法中添加如下代码
image = [[UIImage alloc] initWithData:data];
if (data.length/1024 > 128) {
image = [self compressImageWith:image]; // 压缩过大的图片数据
}
总结:通过解码控制图片大小和限制图片过大(最优下,图片画质不会很差),才能完美解决问题