近期业务做了评论模块,评论内容跟其他APP基本一致,就是评分、文字以及图片,每条评论可以至多包含6张图片,在评论列表会展示评论星级、用户头像、昵称、评论文字、图片以及评论时间,逻辑非常简单,一天搞完,提交打包给测试验证,基本无有效bug,第二天下午测试跑过来说在iPhoneSE上有一个商品,点击后滑动就必会crash,我开始以为是图片数组越界导致,但是测试说别的手机不会crash,我去bugly查看crash信息,发现bugly空空如也,并无crash上报,无奈之下只有真机debug了,找到对应商品,点击进详情页然后滑动,果然crash了,但是xcode并未捕捉到crash堆栈,猜测是内存问题,于是在didreceivememeryWarning方法出断点,再次启动,重新操作,不出意外,crash了,但是还是未走到断点处,最后,打开所有的异常断点,再次启动测试,这次捕捉到了,是sdwebimage 代码在解压图片的时候内存申请失败,在console也出现了terminate due to memery issue,原因找到了怎么解决呢?于是开始以下尝试:
- 是否是SE内存太小,SD内存cache太多导致?于是设置SDCache最多为6张(一条评论数据的图片),然后重新启动,验证,依然crash。
- 是否是内存峰值过高导致?于是在每条cell更新的时候加上了autoreleasepool,接着启动,测试,crash继续。
- 是否是某张图片太大导致?于是把每张图片都download下来,查看图片大小,但是没有发现某张图片dataSize非常大,但总算有结果了,原来每张图片大小有500k,终端上传的时候做了500k的上限限制,于是想着裁剪图片。
- 最后网络上找到了WWDC针对大图片优化的最佳实践,就是下采样,主要代码如下
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, nil); CFDictionaryRef options = (__bridge CFDictionaryRef) @{ (id) kCGImageSourceCreateThumbnailWithTransform : @YES, (id) kCGImageSourceCreateThumbnailFromImageAlways : @YES, (id) kCGImageSourceThumbnailMaxPixelSize : @(60*[UIScreen mainScreen].scale) }; CGImageRef scaledImageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, options); UIImage *scaled = [UIImage imageWithCGImage:scaledImageRef]; CGImageRelease(scaledImageRef);
创建缩略图以后,再次验证,crash问题没有了,但是出现了别的问题,cell滚动后重新加载会显示placeholder,然后再显示图片,这是没有加缓存的问题,于是,运用SDImagecache给缩略图加上缓存,再次运行,完美解决。最后附上代码
@interface ThumbImageManager : NSObject
+ (instancetype)sharedManager;
- (void)thumbImageWithUrl:(NSString *)urlString
width:(float)width
block:(void(^)(NSString *url,UIImage *image))callBack;
@end
#import "ThumbImageManager.h"
@interface ThumbImageManager ()
@property(nonatomic,strong)NSOperationQueue *queue;
@end
@implementation ThumbImageManager
+ (instancetype)sharedManager{
static ThumbImageManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[ThumbImageManager alloc] init];
});
return manager;
}
- (instancetype)init{
self = [super init];
if (self) {
_queue = [[NSOperationQueue alloc] init];
_queue.maxConcurrentOperationCount = 6;
}
return self;;
}
- (void)thumbImageWithUrl:(NSString *)urlString width:(float)width block:(void(^)(NSString *url,UIImage *image))callBack{
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSString *path = [[urlString md5String] stringByAppendingString:@"thumb"];
UIImage *image = [[SDImageCache sharedImageCache] imageFromCacheForKey:path];
if (image) {
callBack(urlString,image);
}else{
NSURL *url = [NSURL URLWithString:urlString];
CGImageSourceRef source = CGImageSourceCreateWithURL((__bridge CFURLRef)url, nil);
CFDictionaryRef options = (__bridge CFDictionaryRef) @{
(id) kCGImageSourceCreateThumbnailWithTransform : @YES,
(id) kCGImageSourceCreateThumbnailFromImageAlways : @YES,
(id) kCGImageSourceThumbnailMaxPixelSize : @(width*[UIScreen mainScreen].scale)
};
CGImageRef scaledImageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, options);
UIImage *scaled = [UIImage imageWithCGImage:scaledImageRef];
CGImageRelease(scaledImageRef);
[[SDImageCache sharedImageCache] storeImage:scaled forKey:path completion:nil];
callBack(urlString,scaled);
}
}];
[_queue addOperation:op];
}
@end