iOS图片设置圆角性能问题

一般我们在iOS开发的过程中设置圆角都是如下这样设置的。

 avatarImageView.clipsToBounds = YES;
 [avatarImageView.layer setCornerRadius:50];

 这样设置会触发离屏渲染,比较消耗性能。比如当一个页面上有十几头像这样设置了圆角
 会明显感觉到卡顿。

 注意:png图片UIImageView处理圆角是不会产生离屏渲染的。(ios9.0之后不会离屏渲染,ios9.0之前还是会离屏渲染)。

所有如果要高性能的设置圆角就需要找另外的方法了。下面是我找到的一些方法并写了一个例子。

IMG_1816.PNG

设置圆角的方法

  • 直接使用setCornerRadius
    这种就是最常用的,也是最耗性能的。

  • setCornerRadius设置圆角之后,shouldRasterize=YES光栅化

    avatarImageView.clipsToBounds = YES;
    [avatarImageView.layer setCornerRadius:50];
    avatarImageView.layer.shouldRasterize = YES;
    avatarImageViewUrl.layer.rasterizationScale=[UIScreen mainScreen].scale;  //UIImageView不加这句会产生一点模糊
    
    shouldRasterize=YES设置光栅化,可以使离屏渲染的结果缓存到内存中存为位图,
    使用的时候直接使用缓存,节省了一直离屏渲染损耗的性能。
    
    但是如果layer及sublayers常常改变的话,它就会一直不停的渲染及删除缓存重新
    创建缓存,所以这种情况下建议不要使用光栅化,这样也是比较损耗性能的。
    
  • 直接覆盖一张中间为圆形透明的图片(推荐使用)
    这种方法就是多加了一张透明的图片,GPU计算多层的混合渲染blending也是会消耗
    一点性能的,但比第一种方法还是好上很多的。

  • UIImage drawInRect绘制圆角
    这种方式GPU损耗低内存占用大,而且UIButton上不知道怎么绘制,可以用
    UIimageView添加个点击手势当做UIButton使用。

    UIGraphicsBeginImageContextWithOptions(avatarImageView.bounds.size, NO, [UIScreen mainScreen].scale);
    [[UIBezierPath bezierPathWithRoundedRect:avatarImageView.bounds
                                  cornerRadius:50] addClip];
    [image drawInRect:avatarImageView.bounds];
    avatarImageView.image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    这段方法可以写在SDWebImage的completed回调里,在主线程异步绘制。
    也可以封装到UIImageView里,写了个DSRoundImageView。后台线程异步绘制,不会阻塞主线程。
    

问题:这种方法图片很多的话CUP消耗会高,内存占用也会暴增,而且后台线程绘制会比在主线程绘制占用更多的内存,不知道怎么解决?求大神指教!

  • SDWebImage处理图片时Core Graphics绘制圆角

      //UIImage绘制为圆角
      int w = imageSize.width;
      int h = imageSize.height;
      int radius = imageSize.width/2;
      
      UIImage *img = image;
      CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
      CGContextRef context = CGBitmapContextCreate(NULL, w, h, 8, 4 * w, colorSpace, kCGImageAlphaPremultipliedFirst);
      CGRect rect = CGRectMake(0, 0, w, h);
      
      CGContextBeginPath(context);
      addRoundedRectToPath(context, rect, radius, radius);
      CGContextClosePath(context);
      CGContextClip(context);
      CGContextDrawImage(context, CGRectMake(0, 0, w, h), img.CGImage);
      CGImageRef imageMasked = CGBitmapContextCreateImage(context);
      img = [UIImage imageWithCGImage:imageMasked];
      
      CGContextRelease(context);
      CGColorSpaceRelease(colorSpace);
      CGImageRelease(imageMasked);
    

    以上代码我写成了UIImage的类别:UIImage+DSRoundImage.h
    并在SDWebImage库里处理image的时候使用类别方法绘制圆角并缓存。


使用Instruments的Core Animation查看性能

  • Color Offscreen-Rendered Yellow
    开启后会把那些需要离屏渲染的图层高亮成黄色,这就意味着黄色图层可能存在性能问题。

  • Color Hits Green and Misses Red
    如果shouldRasterize被设置成YES,对应的渲染结果会被缓存,如果图层是绿色,就表示这些缓存被复用;如果是红色就表示缓存会被重复创建,这就表示该处存在性能问题了。

用Instruments测试得

  • 第一种方法,UIImageView和UIButton都高亮为黄色。

  • 第二种方法,UIImageView和UIButton都高亮为绿色

  • 第三种方法,无任何高亮,说明没离屏渲染。
    这种圆片覆盖的方法一般只用在底色为纯色的时候,如果圆角图片的父View是张图片的时候就没办法了,而且底色如果是多种颜色的话那要做多张不同颜色的圆片覆盖。(可以用代码取底色的颜色值给圆片着色)

  • 第四种方法无任何高亮,说明没离屏渲染(但是CPU消耗和内存占用会很大)

  • 第五种方法无任何高亮,说明没离屏渲染,而且内存占用也不大。(暂时感觉是最优方法)


问题回复:

  • 有回复提到还有一种mask方法。
    这种方法比第一种方法其实更卡顿。一次mask发生了两次离屏渲染和一次主屏渲染。 具体可以参考小心别让圆角成了你列表的帧数杀手

  • @nerozhao说第四种比第一种更卡。

    我刚在demo里加了个例子测试了一下,第一种能明显的感觉到卡顿,第四种还是挺顺畅
    的,有兴趣的可以自己试试看。第四种是解决了离屏渲染GPU的问题。
    

可以用Instruments的 GPU Driver进行测试:

  • Renderer Utilization
    如果这个值超过了~50%,就意味着你的动画可能对帧率有所限制,很可能因为离屏渲染或者是重绘导致的过度混合。
  • Tiler Utilization
    如果这个值超过了~50%,就意味着你的动画可能限制于几何结构方面,也就是在屏幕上有太多的图层占用了。
Instruments

图上面一部分是第一种方法的数据,下面一部分是第四种方法的数据。
第一种方法的Renderer Utilization 和 Tiler Utilization 基本在90%左右。帧率在20左右。
第四种方法的Renderer Utilization 和 Tiler Utilization 基本在20%左右。帧率接近60。
帧率越接近60滑动越顺畅。

  但是经过跟@nerozhao的讨论发现第四种Core Graphics绘制圆角会有大量的内存占用,
  而且每次绘制的时候CUP消耗会很大。

  由于@nerozhao使用了UITableView进行测试,因为UITableView滚动的时候是一直在
  复用的,UIImageView会重复绘制,所以会一直消耗CUP,然后你就能看的明显的卡顿。

  @nerozhao在UITableView里图片的绘制在后台线程进行绘制,解决了卡顿问题,但是
  由于是在后台线程的异步绘制所以在滚动的时候会看到图片先是正方形然后再变成圆形。

  而我使用的是UIScrollerView进行的测试,只有第一次绘制的时候会占用CUP资源,
  所以滑动的时候还是挺流畅的,但是内存消耗还是很大。如果是主线程绘制的话会阻塞一
  点时间的主线程,而后台线程绘制的话内存消耗会更大,特别容易崩溃。

所以第四种方法当图片特别多的时候很容易Received memory warning导致崩溃


解决问题参考文章

  • UIImage drawInRect绘制圆角内存暴增问题

文章:
内存恶鬼drawRect - 谈画图功能的内存优化


最后

关于研究过程及各种设置圆角方法的例子测试对比 github源码
如果我的文章对你有帮助欢迎github Star
如果你有什么问题或者想交流的可以联系我。QQ:398411773

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容