废话不多说,直奔主题。不想看过程的也可以直接划到底部看总结。
这两天新拿到一份代码,在上报发布的模块页面,编辑相册图片(添加多行水印)的时候,程序内存峰值直接飙升到四百多兆。身边的同事告诉我,是因为调用下图所示的封装方法导致的,希望我能来优化一下。
//1.开启上下文
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
//2.绘制图片
[image drawInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
//添加水印文字
[text drawAtPoint:point withAttributes:attributed];
//3.从上下文中获取新图片
UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();
//4.关闭图形上下文
UIGraphicsEndImageContext();
//返回图片
return newImage;
我运行程序看一下这个功能模块,内存确实直接飙到了四百多M。
于是就按照同事讲的先试着从这个方法入手。
首先想到的是使用 autoreleasepool,因为给图片添加多行水印是通过循环添加的,所以我想使用自动释放池就可以在循环结束后释放内存。结果无效,内存无法被回收。然后我开始测试具体是哪句代码导致内存疯长,经过排除,最终定位是由于绘制UIImage的drawInRect方法。之后就开始想有没有什么好的方法替代她,经过查看了一些相册开源库发现很多也都是直接用这句代码来处理图片,于是就放弃了。最后开始来看项目中该模块的代码,看能不能从代码逻辑上进行优化处理(代码比较老,整个功能模块的代码行数达到两千多行)。经过对照功能需求反复查看原代码,发现了添加水印的时候,因为要求展示多行文字,所以每添加一次水印都重新绘制了一次图片。于是我认为可以从这里作为切入点进行优化,我把多行文字统一处理,将多次图片处理合并成一次,最终内存峰值由四百多兆降到了一百多兆,性能提高了70%。效果如下图所示:
【总结思路】使用drawInRect绘制图片,每绘画生成一次新图就涨一次内存,连续多次绘制就会导致内存飙升。这个时候只需要将多次处理尽量合并成一次处理即可。