AsyncDisplayKit -- ( facebook/新浪微博 ) 优化UI性能

   AsyncDisplayKit 是facebook,为了自身的app 《Paper》提高在iOS系统上UI刷新显示的性能,结合runtime 和 runloop 以及UIKit,封装制作了一个开源的异步显示UI的库。

如果你的项目中,多处用到了图片渲染,以及显示UI元素之前有很多计算操作,那么你就可以考虑使用 AsyncDisplayKit。当然 如果你不追求性能的优化,直接使用UIKit 也是可以的。提高性能,可能对于用户来说,感觉不明显,但是对于技术人员来说,性能优化,能够在一些细节之处,提高自己的技能。记得 以前在 新浪实习的日子,第一次做项目时,第一点就是被告知:要注意性能优化!

        好了,说说 AsyncDisplayKit 的大致原理:

UI 线程中一旦出现繁重的任务就会导致界面卡顿,这类任务通常分为3类:排版,绘制,UI对象操作。

排版通常包括计算视图大小、计算文本高度、重新计算子式图的排版等操作。

绘制一般有文本绘制 (例如 CoreText)、图片绘制 (例如预先解压)、元素绘制 (Quartz)等操作。

UI对象操作通常包括 UIView/CALayer 等 UI 对象的创建、设置属性和销毁。

其中前两类操作可以通过各种方法扔到后台线程执行,而最后一类操作只能在主线程完成,并且有时后面的操作需要依赖前面操作的结果 (例如TextView创建时可能需要提前计算出文本的大小)。ASDK 所做的,就是尽量将能放入后台的任务放入后台,不能的则尽量推迟 (例如视图的创建、属性的调整)。

为此,ASDK 创建了一个名为 ASDisplayNode 的对象,并在内部封装了 UIView/CALayer,它具有和 UIView/CALayer 相似的属性,例如 frame、backgroundColor等。所有这些属性都可以在后台线程更改,开发者可以只通过 Node 来操作其内部的 UIView/CALayer,这样就可以将排版和绘制放入了后台线程。但是无论怎么操作,这些属性总需要在某个时刻同步到主线程的 UIView/CALayer 去。

ASDK 仿照 QuartzCore/UIKit 框架的模式,实现了一套类似的界面更新的机制:即在主线程的 RunLoop 中添加一个 Observer,监听了 kCFRunLoopBeforeWaiting 和 kCFRunLoopExit 事件,在收到回调时,遍历所有之前放入队列的待处理的任务,然后一一执行。


AsyncDisplayKit 对UI的优化,达到了步步逼近超级响应UI的极限。能够让tableview 以及 collectionview的滑动 达到 60 FPS 的滚动帧率。完全 优化了 tableview的滑动,简直就是 :如丝般顺滑。 这里我提一款 app 叫 《糗百》,可能大家很多人都知道,我是在pad上安装的这个app,pad的系统是ios7.1,但……哎!这款app就存有一个很明显的tableview迟缓,很明显,不得不批评一下这款app的产品,对性能优化的不重视。 这个款app有一个很明显导致 tableview 卡顿的现象。 那就是 每一个cell 上有图片,而这个imageview的 frmae 是根据image的大小来确定的,图片使用异步加载(sdweb 或者 af )是没错,但是如果你的计算图片大小放到了主线程中,那么图片少美呦什么问题,而这里每一个cell都有图片。

图:


这时候,我根本滑不动。tableview 出现了卡顿。 反观 微博,微信,每个cell的复杂度 比 这个都要复杂,但是 很少遇见 微博,微信 出现滑动卡顿现象,即使是在ios7.1的系统上仍然流畅。


关于 AsyncDisplayKit ,很抱歉,长城防火墙拦截了 facebook,所以你想更多的了解它,正规途径 是不太可能的。 但是…………你懂的。如果可以,给出一份官方指导文档:

http://asyncdisplaykit.org/appledoc/


AsyncDisplayKit 的github地址:https://github.com/facebook/AsyncDisplayKit


AsyncDisplayKit 中的 ASImageNode 对图片处理 简直是我目前见过的框架处理的最好的(没有之一),下面会对比到。

使用 基本的 ASTextNode 和 ASImageNode 来和 UIKit 的 UILabel 以及 UIImageView 来对比:

UIKit:

UILabel *label=(UILabel *)objc_msgSend(objc_msgSend([UILabel class], sel_registerName("alloc")), sel_registerName("init"));
    
    label.text=@"--   UIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKit   --";
    objc_msgSend(label, sel_registerName("setTextAlignment:"),NSTextAlignmentCenter);
    
    CGSize size=[self textSize:label.text];
    label.frame=(CGRect){CGPointMake(0, 40),size};
    label.numberOfLines=0;
    
    
    objc_msgSend(self.view,sel_registerName("addSubview:"),label);
    
    
    UIImageView *imageView=(UIImageView *)objc_msgSend([UIImageView class], sel_registerName("new"));
    imageView.frame=CGRectMake((self.view.bounds.size.width-100)/2, 80, 100, 100);
    imageView.image=[UIImage imageNamed:@"test"];
//    imageView.contentMode=UIViewContentModeScaleAspectFit;
    objc_msgSend(self.view, sel_registerName("addSubview:"),imageView);

 


AsyncDisplayKit:

// AsyncDisplayKit
    
    ASTextNode *textNode=(ASTextNode *)objc_msgSend(objc_msgSend([ASTextNode class], sel_registerName("alloc")), sel_registerName("init"));
    
    
    textNode.attributedString=[[NSAttributedString alloc] initWithString:@"--   AsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKitAsyncDisplayKit   --" attributes:@{NSFontAttributeName:[UIFont systemFontOfSize:17.0]}];
    [textNode measure:CGSizeMake(self.view.bounds.size.width, FLT_MAX)];
    
    textNode.frame=(CGRect){CGPointMake((self.view.bounds.size.width-textNode.calculatedSize.width)/2, 220),textNode.calculatedSize};
    
    objc_msgSend(self.view, sel_registerName("addSubview:"),textNode.view);
    
    
    
    ASImageNode *imageNode=(ASImageNode *)objc_msgSend([ASImageNode class], sel_registerName("new"));
    
   
//    imageNode.backgroundColor=[UIColor lightGrayColor];
    imageNode.image=[UIImage imageNamed:@"test"];
    
//    [imageNode measure:CGSizeMake(self.view.bounds.size.width, FLT_MAX)];
//    
//    imageNode.frame=(CGRect){CGPointZero,imageNode.calculatedSize};

//    imageNode.contentMode=UIViewContentModeScaleAspectFill;
    imageNode.frame=CGRectMake((self.view.bounds.size.width-100)/2, 260, 100, 100);
    
    
    objc_msgSend(self.view, @selector(addSubview:),imageNode.view);
    


效果 图:





可以看见 在 我们经常使用的 UIKt 下的UILabel 自适应 在 主线程中先计算了 size ,然后 还需要设置显示行数为0,然后在显示到view上。如果这样试试:

 UILabel *label=(UILabel *)objc_msgSend(objc_msgSend([UILabel class], sel_registerName("alloc")), sel_registerName("init"));

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        
        label.text=@"--   UIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKitUIKit   --";
        objc_msgSend(label, sel_registerName("setTextAlignment:"),NSTextAlignmentCenter);
        
        CGSize size=[self textSize:label.text];
        label.frame=(CGRect){CGPointMake(0, 40),size};
        label.numberOfLines=0;
        
        dispatch_async(dispatch_get_main_queue(), ^{
            
            objc_msgSend(self.view,sel_registerName("addSubview:"),label);
            
        });
        
    });

这样的话 计算操作异步执行了,只是必须的操作 放到了主线程中执行。



难道 每一个控件 我们都需要来这么写吗?这样的话,代码量会增加很多, AsyncDisplayKit 基本原理就是: 将不是必须在主线程执行的操作,放到异步线程中执行。我们直接使用即可。



再来看看 ASImageNode 。


好了,由于时间有限,现在还在公司上班呢。我将工程 分享,又兴趣的朋友可以研究下。 这对于 如何提高 ui 的性能 十分有帮助。


demo 地址:http://download.csdn.net/detail/u010165653/8805351



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值