YYKit的微博实例记录

查看YYKit的微博实例,学习tableView如何优化:

一.预排版

  1. 获取到json——》后台线程对每个cell数据封装为一个布局对象 CellLayout,缓存到内存,以供稍后使用:
    1)封装为model:WBTimeLineItem
    2)计算布局:WBStatueLayout;属性里面有model、布局结果

TableView 在请求各个高度函数时,不会消耗任何多余计算量;当把 CellLayout 设置到 Cell 内部时,Cell 内部也不用再计算布局了。

  1. 其他
    cell里面的每部分view都进行封装;并和cell放在一个类文件里面;
    model里面的每个小model,也和大model放在一个类文件里面;
    布局的layout文件,也是大类文件里面包含小的;

cell上面布局如下:


cell布局

二.预渲染:

1.圆角头像:

YYKit的demo里面是把头像下载后在后台线程渲染为圆型后,放到ImageCache缓存中。

对于 TableView 来说,Cell 内容的离屏渲染会带来较大的 GPU 消耗。尽量避免使用 layer 的 border、corner、shadow、mask 等技术,而尽量在后台线程预先绘制好对应内容。

-方法1: controller里面削圆具体操作如下(自己写的):

- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIImage *image = [UIImage imageNamed:@"11_1684_773"];
  
    UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    imageView.image = [self getImage:image];//削圆

    [self.view addSubview:imageView];
    
}
//削圆
-(UIImage *)getImage:(UIImage *)image{
    
    //1、开启上下文
    UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);

    //2、设置裁剪区域(rect:圆的x,y,直径)
    UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 512, 512)];
    [path addClip];

    //3、绘制图片
    [image drawAtPoint:CGPointZero];

    //4、获取新图片
    UIImage * newImage = UIGraphicsGetImageFromCurrentImageContext();

    //5、关闭上下文
    UIGraphicsEndImageContext();

    //6、返回新图片
    return newImage;
    
}
  • 方法2:使用UIImage的分类操作如下(YYKit微博demo里面这么写):
.h:
@interface UIImage (TT)
- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius;
@end

.m:
#import "UIImage+TT.h"
@implementation UIImage (TT)

- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius {
    return [self imageByRoundCornerRadius:radius borderWidth:0 borderColor:nil];
}

- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius
                          borderWidth:(CGFloat)borderWidth
                          borderColor:(UIColor *)borderColor {
    return [self imageByRoundCornerRadius:radius
                                  corners:UIRectCornerAllCorners
                              borderWidth:borderWidth
                              borderColor:borderColor
                           borderLineJoin:kCGLineJoinMiter];
}
//把图片预渲染为圆形
- (UIImage *)imageByRoundCornerRadius:(CGFloat)radius
                              corners:(UIRectCorner)corners
                          borderWidth:(CGFloat)borderWidth
                          borderColor:(UIColor *)borderColor
                       borderLineJoin:(CGLineJoin)borderLineJoin {
    
    if (corners != UIRectCornerAllCorners) {
        UIRectCorner tmp = 0;
        if (corners & UIRectCornerTopLeft) tmp |= UIRectCornerBottomLeft;
        if (corners & UIRectCornerTopRight) tmp |= UIRectCornerBottomRight;
        if (corners & UIRectCornerBottomLeft) tmp |= UIRectCornerTopLeft;
        if (corners & UIRectCornerBottomRight) tmp |= UIRectCornerTopRight;
        corners = tmp;
    }
    
    UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    
    CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);
    
    CGContextScaleCTM(context, 1, -1);// 缩放
    CGContextTranslateCTM(context, 0, -rect.size.height);//平移
    
    CGFloat minSize = MIN(self.size.width, self.size.height);
    if (borderWidth < minSize / 2.0) {//削圆
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:CGRectInset(rect, 0, borderWidth) byRoundingCorners:corners cornerRadii:CGSizeMake(radius, borderWidth)];

        [path closePath];
        
        CGContextSaveGState(context);
        [path addClip];
        CGContextDrawImage(context, rect, self.CGImage);
        CGContextRestoreGState(context);
        
        
    }
    
    if (borderColor && borderWidth < minSize / 2 && borderWidth > 0) {
        CGFloat strokeInset = (floor(borderWidth * self.scale) + 0.5) / self.scale;
        CGRect strokeRect = CGRectInset(rect, strokeInset, strokeInset);
        CGFloat strokeRadius = radius > self.scale / 2 ? radius - self.scale / 2 : 0;
        UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:strokeRect byRoundingCorners:corners cornerRadii:CGSizeMake(strokeRadius, borderWidth)];
        [path closePath];

        path.lineWidth = borderWidth;
        path.lineJoinStyle = borderLineJoin;
        [borderColor setStroke];
        [path stroke];
    }
    
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

@end
viewcontroller里面:
- (void)viewDidLoad {
    [super viewDidLoad];
    
    UIImage *image = [UIImage imageNamed:@"11_1684_773"];    
    UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    imageView.image =  [image imageByRoundCornerRadius:image.size.width/2.0];
    [self.view addSubview:imageView];
    
}
效果

2.其他削圆方法

1.最常见的方法:

    UIImageView * imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
    imageView.image =  [UIImage imageNamed:@"11_1684_773"];
    //削圆
    imageView.layer.masksToBounds = YES;
    imageView.layer.cornerRadius = 50;
    
    [self.view addSubview:imageView];

这种对图片进行削圆会产生离屏渲染。如果不是tableView上面进行削圆,削圆的视图不是很多,直接这么操作即可。

2.使用CAShapeLayer和UIBezierPath设置圆角

    //1.创建削圆的曲线
    UIBezierPath * maskPath = [UIBezierPath bezierPathWithOvalInRect:self.imageView.bounds];
    
    //2.创建shaperLayer
    CAShapeLayer * maskLayer = [[CAShapeLayer alloc] init];
    //设置shaperLayer大小
    maskLayer.frame = self.imageView.bounds;
    //把曲线设置为shaperLayer的削圆曲线
    maskLayer.path = maskPath.CGPath;
    
    //3.把shaperLayer设置为imageView.layer.mask
    self.imageView.layer.mask = maskLayer;
    self.imageView.image = [UIImage imageNamed:@"1.png"];

使用的是设置蒙层的方法,其实也会产生离屏渲染。

异步绘制

下面这几部分还没有学会,道行太浅,需要把每个部分细致的学习后再回来慢慢咀嚼其中的好处;

demo里面把显示文本的控件上用到了异步绘制的功能;

全局并发控制

大量的任务提交到后台队列时,某些任务会因为某些原因(此处是 CGFont 锁)被锁住导致线程休眠,或者被阻塞,concurrent queue 随后会创建新的线程来执行其他任务。

这样App 在同一时刻就会存在几十个线程同时运行、创建、销毁。这些操作仍然会挤占掉主线程的 CPU 资源。

YYKit作者的 YYDispatchQueuePool,为不同优先级创建和 CPU 数量相同的 serial queue,每次从 pool 中获取 queue 时,会轮询返回其中一个 queue。
把 App 内所有异步操作,包括图像解码、对象释放、异步绘制等,都按优先级不同放入了全局的 serial queue 中执行,这样尽量避免了过多线程导致的性能问题。

更高效的异步图片加载

显示简单的单张图片时,利用 UIView.layer.contents就足够;使用 UIImageView 反而会带来额外的资源消耗;
YYKite作者实现了一个性能更高的图片加载库;在 CALayer 上添加了 setImageWithURL 等方法。

其他学习到的点:

  • 有不少视觉元素并不需要触摸事件,这些元素可以用 ASDK(第三方) 的图层合成技术预先绘制为一张图
  • 减少每个 Cell 内图层的数量,用 CALayer 替换掉 UIView
  • 把 Cell 按类型划分,进一步减少 Cell 内不必要的视图对象和操作

后续还需要学习:

  • 简单的 FPS 指示器:FPSLabel

  • 在后台线程操作对图片削圆,然后放到ImageCache缓存中

  • 使用CADisplayLink显示FPS
    -为了达到最高性能,你可能需要牺牲一些开发速度,不要用 Autolayout 等技术,少用 UILabel 等文本控件,所以还需要学习 layer的使用

  • Quartz 2D

  • YYKit的学习:YYLayout、YYDispatchQueuePool
    、里面对图片的加载和解码的使用

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

推荐阅读更多精彩内容

  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,383评论 8 265
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,100评论 1 32
  • 静静的躺着 我望着天花板,望着灯光 发呆、沉默 静静的想着 我看着手机,胡乱地翻着 感伤、难言 静静的 我静静的 ...
    破钟阅读 274评论 0 0
  • 一位大学生暑假到云南参加社会实践活动。联欢时要表演节目,性格内向的他一时慌了手脚,说了这么段话:“我这个人从小就崇...
    24K超超老师阅读 304评论 0 0
  • 文/初漪 丁香 丁香, 丁香花开了 江南, 你不懂 你那里的花开花落 总是太快 二十一点,下课 约好寻找五瓣花 穿...
    初漪阅读 951评论 0 6