# AsyncDisplayKit **Repository Path**: marklewis/AsyncDisplayKit ## Basic Information - **Project Name**: AsyncDisplayKit - **Description**: 异步渲染框架兼容iOS 4系统 - **Primary Language**: Objective-C - **License**: BSD-3-Clause - **Default Branch**: version/v2010-04-dp1 - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-04-16 - **Last Updated**: 2025-09-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AsyncDisplayKit ![AsyncDisplayKit] 中文文档修改 https://www.cnblogs.com/fengyunyu/p/4051140.html ## tableView的性能优化 最近在微博上看到一个很好的开源项目VVeboTableViewDemo,是关于如何优化UITableView的。加上正好最近也在优化项目中的类似朋友圈功能这块,思考了很多关于UITableView的优化技巧,相信这块是难点也是痛点,所以决定详细的整理下我对优化UITableView的理解。 UITableView作为iOS开发中最重要的控件之一,其中的实现原理很是考究。Apple在这块的优化水平直接决定了iOS的体验能甩安卓几条街,哈哈,扯淡扯多了。。。好了,废话不多说,直接进入主题。首先来谈谈我对UITableView的认识: UITableView的简单认识 UITableView最核心的思想就是UITableViewCell的重用机制。简单的理解就是:UITableView只会创建一屏幕(或一屏幕多一点)的UITableViewCell,其他都是从中取出来重用的。每当Cell滑出屏幕时,就会放入到一个集合(或数组)中(这里就相当于一个重用池),当要显示某一位置的Cell时,会先去集合(或数组)中取,如果有,就直接拿来显示;如果没有,才会创建。这样做的好处可想而知,极大的减少了内存的开销。 知道UITableViewCell的重用原理后,我们来看看UITableView的回调方法。UITableView最主要的两个回调方法是tableView:cellForRowAtIndexPath:和tableView:heightForRowAtIndexPath:。理想上我们是会认为UITableView会先调用前者,再调用后者,因为这和我们创建控件的思路是一样的,先创建它,再设置它的布局。但实际上却并非如此,我们都知道,UITableView是继承自UIScrollView的,需要先确定它的contentSize及每个Cell的位置,然后才会把重用的Cell放置到对应的位置。所以事实上,UITableView的回调顺序是先多次调用tableView:heightForRowAtIndexPath:以确定contentSize及Cell的位置,然后才会调用tableView:cellForRowAtIndexPath:,从而来显示在当前屏幕的Cell。 举个例子来说:如果现在要显示100个Cell,当前屏幕显示5个。那么刷新(reload)UITableView时,UITableView会先调用100次tableView:heightForRowAtIndexPath:方法,然后调用5次tableView:cellForRowAtIndexPath:方法;滚动屏幕时,每当Cell滚入屏幕,都会调用一次tableView:heightForRowAtIndexPath:、tableView:cellForRowAtIndexPath:方法。 看到这里,想必大伙也都能隐约察觉到,UITableView优化的首要任务是要优化上面两个回调方法。事实也确实如此,下面按照我探讨进阶的过程,来研究如何优化: 优化探索,项目拿到手时代码是这样: ```c - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { ContacterTableCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ContacterTableCell"]; if (!cell) { cell = (ContacterTableCell *)[[[NSBundle mainBundle] loadNibNamed:@"ContacterTableCell" owner:self options:nil] lastObject]; } NSDictionary *dict = self.dataList[indexPath.row]; [cell setContentInfo:dict]; return cell; } - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath]; return cell.frame.size.height; } ``` 看到这段代码,对于刚毕业的我来说,觉得还是蛮巧妙的,但巧归巧,当Cell非常复杂的时候,直接卡出翔了。。。特别是在我的Touch4上,这我能忍?!好吧,依据上面UITableView原理的分析,我们先来分析它为什么卡? 这样写,在Cell赋值内容的时候,会根据内容设置布局,当然也就可以知道Cell的高度,想想如果1000行,那就会调用1000+页面Cell个数次tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath方法,而我们对Cell的处理操作,都是在这个方法里的!什么赋值、布局等等。开销自然很大,这种方案Pass。。。改进代码。 改进代码后: ``` c - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *dict = self.dataList[indexPath.row]; return [ContacterTableCell cellHeightOfInfo:dict]; } ``` 思路是把赋值和计算布局分离。这样让tableView:cellForRowAtIndexPath:方法只负责赋值,tableView:heightForRowAtIndexPath:方法只负责计算高度。注意:两个方法尽可能的各司其职,不要重叠代码!两者都需要尽可能的简单易算。Run一下,会发现UITableView滚动流畅了很多。。。 基于上面的实现思路,我们可以在获得数据后,直接先根据数据源计算出对应的布局,并缓存到数据源中,这样在tableView:heightForRowAtIndexPath:方法中就直接返回高度,而不需要每次都计算了。 ``` c - (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath { NSDictionary *dict = self.dataList[indexPath.row]; CGRect rect = [dict[@"frame"] CGRectValue]; return rect.frame.height; } ``` 其实上面的改进方法并不是最佳方案,但基本能满足简单的界面!记得开头我的任务吗?像朋友圈那样的图文混排,这种方案还是扛不住的!我们需要进入更深层次的探究:自定义Cell的绘制。 我们在Cell上添加系统控件的时候,实质上系统都需要调用底层的接口进行绘制,当我们大量添加控件时,对资源的开销也会很大,所以我们可以索性直接绘制,提高效率。是不是说的很抽象?废话不多说,直接上代码: 首先需要给自定义的Cell添加draw方法,(当然也可以重写drawRect)然后在方法体中实现: ``` c //异步绘制 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ CGRect rect = [_data[@"frame"] CGRectValue]; UIGraphicsBeginImageContextWithOptions(rect.size, YES, 0); CGContextRef context = UIGraphicsGetCurrentContext(); //整个内容的背景 [[UIColor colorWithRed:250/255.0 green:250/255.0 blue:250/255.0 alpha:1] set]; CGContextFillRect(context, rect); //转发内容的背景 if ([_data valueForKey:@"subData"]) { [[UIColor colorWithRed:243/255.0 green:243/255.0 blue:243/255.0 alpha:1] set]; CGRect subFrame = [_data[@"subData"][@"frame"] CGRectValue]; CGContextFillRect(context, subFrame); [[UIColor colorWithRed:200/255.0 green:200/255.0 blue:200/255.0 alpha:1] set]; CGContextFillRect(context, CGRectMake(0, subFrame.origin.y, rect.size.width, .5)); } { //名字 float leftX = SIZE_GAP_LEFT+SIZE_AVATAR+SIZE_GAP_BIG; float x = leftX; float y = (SIZE_AVATAR-(SIZE_FONT_NAME+SIZE_FONT_SUBTITLE+6))/2-2+SIZE_GAP_TOP+SIZE_GAP_SMALL-5; [_data[@"name"] drawInContext:context withPosition:CGPointMake(x, y) andFont:FontWithSize(SIZE_FONT_NAME) andTextColor:[UIColor colorWithRed:106/255.0 green:140/255.0 blue:181/255.0 alpha:1] andHeight:rect.size.height]; //时间+设备 y += SIZE_FONT_NAME+5; float fromX = leftX; float size = [UIScreen screenWidth]-leftX; NSString *from = [NSString stringWithFormat:@"%@ %@", _data[@"time"], _data[@"from"]]; [from drawInContext:context withPosition:CGPointMake(fromX, y) andFont:FontWithSize(SIZE_FONT_SUBTITLE) andTextColor:[UIColor colorWithRed:178/255.0 green:178/255.0 blue:178/255.0 alpha:1] andHeight:rect.size.height andWidth:size]; } //将绘制的内容以图片的形式返回,并调主线程显示 UIImage *temp = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); dispatch_async(dispatch_get_main_queue(), ^{ if (flag==drawColorFlag) { postBGView.frame = rect; postBGView.image = nil; postBGView.image = temp; } } //内容如果是图文混排,就添加View,用CoreText绘制 [self drawText]; }} ``` 上述代码只贴出来部分功能,但大体的思路都是一样的,各个信息都是根据之前算好的布局进行绘制的。这里是需要异步绘制,但如果在重写drawRect方法就不需要用GCD异步线程了,因为drawRect本来就是异步绘制的。对于图文混排的绘制,可以移步Google,研究下CoreText,这块内容太多了,不便展开。 好了,至此,我们又让UITableView的效率提高了一个等级!但我们的步伐还远远不止这些,下面我们还可以从UIScrollView的角度出发,再次找到突破口。 滑动UITableView时,按需加载对应的内容 直接上代码: ``` c //按需加载 - 如果目标行与当前行相差超过指定行数,只在目标滚动范围的前后指定3行加载。 - (void)scrollViewWillEndDragging:(UIScrollView *)scrollView withVelocity:(CGPoint)velocity targetContentOffset:(inout CGPoint *)targetContentOffset{ NSIndexPath *ip = [self indexPathForRowAtPoint:CGPointMake(0, targetContentOffset->y)]; NSIndexPath *cip = [[self indexPathsForVisibleRows] firstObject]; NSInteger skipCount = 8; if (labs(cip.row-ip.row)>skipCount) { NSArray *temp = [self indexPathsForRowsInRect:CGRectMake(0, targetContentOffset->y, self.width, self.height)]; NSMutableArray *arr = [NSMutableArray arrayWithArray:temp]; if (velocity.y<0) { NSIndexPath *indexPath = [temp lastObject]; if (indexPath.row+33) { [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-3 inSection:0]]; [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-2 inSection:0]]; [arr addObject:[NSIndexPath indexPathForRow:indexPath.row-1 inSection:0]]; } } [needLoadArr addObjectsFromArray:arr]; } } 记得在tableView:cellForRowAtIndexPath:方法中加入判断: if (needLoadArr.count>0&&[needLoadArr indexOfObject:indexPath]==NSNotFound) { [cell clear]; return; } ``` 滚动很快时,只加载目标范围内的Cell,这样按需加载,极大的提高流畅度。 写了这么多,也差不多该来个总结了! UITableView的优化主要从三个方面入手: 提前计算并缓存好高度(布局),因为heightForRowAtIndexPath:是调用最频繁的方法; 异步绘制,遇到复杂界面,遇到性能瓶颈时,可能就是突破口; 滑动时按需加载,这个在大量图片展示,网络加载的时候很管用!(SDWebImage已经实现异步加载,配合这条性能杠杠的)。 除了上面最主要的三个方面外,还有很多几乎大伙都很熟知的优化点: 正确使用reuseIdentifier来重用Cells 尽量使所有的view opaque,包括Cell自身 尽量少用或不用透明图层 如果Cell内现实的内容来自web,使用异步加载,缓存请求结果 减少subviews的数量 在heightForRowAtIndexPath:中尽量不使用cellForRowAtIndexPath:,如果你需要用到它,只用一次然后缓存结果 尽量少用addView给Cell动态添加View,可以初始化时就添加,然后通过hide来控制是否显示 尾巴 肯定很多人会非常好奇,为什么我都是手动用代码创建Cell的?现在主流不都是Xib、Storyboard什么的嘛?我的回答是:要想提高效率,还是手动写有用!抛开Xib、Storyboard需要系统自动转码,给系统多加了一层负担不谈,自定义Cell的绘制更是无从下手,所以,在我看来,复杂的需要高效的界面,还是手动写代码吧!!! 最后如果你们的项目都是用的Xib、Storyboard,并需要优化UITableView的话,sunnyxx大神提出了好的方案: 大伙可以自行研究研究。 知识是需要不断学习的,作为刚上路的我,如果有什么理解不到位的,欢迎大伙留言指正,如果你有什么更牛逼的想法,希望一起交流交流。 注明:本篇的分析源码来源于开源项目VVeboTableViewDemo 参考:https://github.com/johnil/VVeboTableViewDemo --- 在iOS应用中,UITableView应该是使用率最高的视图之一了。iPod、时钟、日历、备忘录、Mail、天气、照片、电话、短信、 Safari、App Store、iTunes、Game Center⋯几乎所有自带的应用中都能看到它的身影,可见它的重要性。 然而在使用第三方应用时,却经常遇到性能上的问题,普遍表现在滚动时比较卡,特别是table cell中包含图片的情况时。 实际上只要针对性地优化一下,这种问题就不会有了。有兴趣的可以看看 LazyTableImages这个官方的例子程序,虽然也要从网上下载图片并显示,但滚动时丝毫不卡。 下面就说说我对UITableView的了解。不过由于我也是初学者,或许会说错或遗漏一些,因此仅供参考。 首先说下UITableView的原理。有兴趣的可以看看 《About Table Views in iOS-Based Applications》。 UITableView是UIScrollView的子类,因此它可以自动响应滚动事件(一般为上下滚动)。 它 内部包含0到多个UITableViewCell对象,每个table cell展示各自的内容。当新cell需要被显示时,就会调用tableView:cellForRowAtIndexPath:方法来获取或创建一个 cell;而不可视时,它又会被释放。由此可见,同一时间其实只需要存在一屏幕的cell对象即可,不需要为每一行创建一个cell。 此 外,UITableView还可以分为多个sections,每个区段都可以有自己的head、foot和cells。而在定位一个cell时,就需要2 个字段了:在哪个section,以及在这个section的第几行。这在iOS SDK中是用NSIndexPath来表述的,UIKit为其添加了indexPathForRow:inSection:这个创建方法。 其他诸如编辑之类的就不提了,因为和本文无关。 介绍完原理,接下来就开始优化吧。 - 使用不透明视图。 不透明的视图可以极大地提高渲染的速度。因此如非必要,可以将table cell及其子视图的opaque属性设为YES(默认值)。 其中的特例包括背景色,它的alpha值应该为1(例如不要使用clearColor);图像的alpha值也应该为1,或者在画图时设为不透明。 - 不要重复创建不必要的table cell。 前面说了,UITableView只需要一屏幕的UITableViewCell对象即可。因此在cell不可见时,可以将其缓存起来,而在需要时继续使用它即可。 而UITableView也提供了这种机制,只需要简单地设置一个identifier即可: ``` c static NSString *CellIdentifier = @"xxx"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];if (cell == nil) { cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease]; } ``` 值得一提的是,cell被重用时,它内部绘制的内容并不会被自动清除,因此你可能需要调用setNeedsDisplayInRect:或setNeedsDisplay方法。 此 外,在添加table cell的时候,如果不需要动画效果,最好不要使用insertRowsAtIndexPaths:withRowAnimation:方法,而是直接调 用reloadData方法。因为前者会对所有indexPaths调用tableView:cellForRowAtIndexPath:方法,即便该 cell并不需要显示(不知道是不是bug),这就可能创建大量多余的cell。勘误:只是在模拟器上测试如此,真机调试时没有这种bug。 - 减少视图的数目。 UITableViewCell包含了textLabel、detailTextLabel和imageView等view,而你还可以自定义一些视图放在它的contentView里。然而view是很大的对象,创建它会消耗较多资源,并且也影响渲染的性能。 如果你的table cell包含图片,且数目较多,使用默认的UITableViewCell会非常影响性能。奇怪的是,使用自定义的view,而非预定义的view,明显会快些。 当然,最佳的解决办法还是继承UITableViewCell,并在其drawRect:中自行绘制: ``` c - (void)drawRect:(CGRect)rect { if (image) { [image drawAtPoint:imagePoint]; self.image = nil; } else { [placeHolder drawAtPoint:imagePoint]; } [text drawInRect:textRect withFont:font lineBreakMode:UILineBreakModeTailTruncation]; } ``` 不过这样一来,你会发现选中一行后,这个cell就变蓝了,其中的内容就被挡住了。最简单的方法就是将cell的selectionStyle属性设为UITableViewCellSelectionStyleNone,这样就不会被高亮了。 此 外还可以创建CALayer,将内容绘制到layer上,然后对cell的contentView.layer调用addSublayer:方法。这个例 子中,layer并不会显著影响性能,但如果layer透明,或者有圆角、变形等效果,就会影响到绘制速度了。解决办法可参见后面的预渲染图像。 - 不要做多余的绘制工作。 在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。 例如上例中,就可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。 - 预渲染图像。 你会发现即使做到了上述几点,当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕,详细做法可见《利用预渲染加速iOS设备的图像显示》。 - 不要阻塞主线程。 做到前几点后,你的table view滚动时应该足够流畅了,不过你仍可能让用户感到不爽。常见的现象就是在更新数据时,整个界面卡住不动,完全不响应用户请求。 出现这种现象的原因就是主线程执行了耗时很长的函数或方法,在其执行完毕前,无法绘制屏幕和响应用户请求。其中最常见的就是网络请求了,它通常都需要花费数秒的时间,而你不应该让用户等待那么久。 解 决办法就是使用多线程,让子线程去执行这些函数或方法。这里面还有一个学问,当下载线程数超过2时,会显著影响主线程的性能。因此在使用 ASIHTTPRequest时,可以用一个NSOperationQueue来维护下载请求,并将其 maxConcurrentOperationCount设为2。而NSURLRequest则可以配合GCD来实现,或者使用NSURLConnection的setDelegateQueue:方法。 当然,在不需要响应用户请求时,也可以增加下载线程数,以加快下载速度: ``` c - (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate { if (!decelerate) { queue.maxConcurrentOperationCount = 5; } } - (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView { queue.maxConcurrentOperationCount = 5; } - (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView { queue.maxConcurrentOperationCount = 2; } ``` 此外,自动载入更新数据对用户来说也很友好,这减少了用户等待下载的时间。例如每次载入50条信息,那就可以在滚动到倒数第10条以内时,加载更多信息: ``` c - (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath { if (count - indexPath.row < 10 && !updating) { updating = YES; [self update]; } }// update方法获取到结果后,设置updating为NO ``` 还有一点要注意的就是当图片下载完成后,如果cell是可见的,还需要更新图像: ``` c NSArray *indexPaths = [self.tableView indexPathsForVisibleRows];for (NSIndexPath *visibleIndexPath in indexPaths) { if (indexPath == visibleIndexPath) { MyTableViewCell *cell = (MyTableViewCell *)[self.tableView cellForRowAtIndexPath:indexPath]; cell.image = image; [cell setNeedsDisplayInRect:imageRect]; break; } }// 也可不遍历,直接与头尾相比较,看是否在中间即可。 ``` 最后还是前面所说过的insertRowsAtIndexPaths:withRowAnimation:方法,插入新行需要在主线程执行,而一次插入很多行的话(例如50行),会长时间阻塞主线程。而换成reloadData方法的话,瞬间就处理完了。 YYKit大神博客 https://blog.ibireme.com/2015/10/23/daily/ [![Build Status](https://travis-ci.org/facebook/AsyncDisplayKit.svg)](https://travis-ci.org/facebook/AsyncDisplayKit) [![Version](http://img.shields.io/cocoapods/v/AsyncDisplayKit.svg)](http://cocoapods.org/?q=AsyncDisplayKit) [![Platform](http://img.shields.io/cocoapods/p/AsyncDisplayKit.svg)]() [![License](http://img.shields.io/cocoapods/l/AsyncDisplayKit.svg)](https://github.com/facebook/AsyncDisplayKit/blob/master/LICENSE) AsyncDisplayKit is an iOS framework that keeps even the most complex user interfaces smooth and responsive. It was originally built to make Facebook's [Paper](https://facebook.com/paper) possible, and goes hand-in-hand with [pop](https://github.com/facebook/pop)'s physics-based animations — but it's just as powerful with UIKit Dynamics and conventional app designs. ### Quick start ASDK is available on [CocoaPods](http://cocoapods.org). Add the following to your Podfile: ```ruby pod 'AsyncDisplayKit' ``` (ASDK can also be used as a regular static library: Copy the project to your codebase manually, adding `AsyncDisplayKit.xcodeproj` to your workspace. Add `libAsyncDisplayKit.a`, AssetsLibrary, and Photos to the "Link Binary With Libraries" build phase. Include `-lc++ -ObjC` in your project linker flags.) Import the framework header, or create an [Objective-C bridging header](https://developer.apple.com/library/ios/documentation/swift/conceptual/buildingcocoaapps/MixandMatch.html) if you're using Swift: ```objective-c #import ``` AsyncDisplayKit Nodes are a thread-safe abstraction layer over UIViews and CALayers: ![node-view-layer diagram](https://github.com/facebook/AsyncDisplayKit/blob/master/docs/assets/node-view-layer.png) You can construct entire node hierarchies in parallel, or instantiate and size a single node on a background thread — for example, you could do something like this in a UIViewController: ```objective-c dispatch_async(_backgroundQueue, ^{ ASTextNode *node = [[ASTextNode alloc] init]; node.attributedString = [[NSAttributedString alloc] initWithString:@"hello!" attributes:nil]; [node measure:CGSizeMake(screenWidth, FLT_MAX)]; node.frame = (CGRect){ CGPointZero, node.calculatedSize }; // self.view isn't a node, so we can only use it on the main thread dispatch_async(dispatch_get_main_queue(), ^{ [self.view addSubview:node.view]; }); }); ``` AsyncDisplayKit at a glance: * `ASImageNode` and `ASTextNode` are drop-in replacements for UIImageView and UITextView. * `ASMultiplexImageNode` can load and display progressively higher-quality variants of an image over a slow cell network, letting you quickly show a low-resolution photo while the full size downloads. * `ASNetworkImageNode` is a simpler, single-image counterpart to the Multiplex node. * `ASTableView` and `ASCollectionView` are a node-aware UITableView and UICollectionView, respectively, that can asynchronously preload cell nodes — from loading network data to rendering — all without blocking the main thread. You can also easily [create your own nodes](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/ASDisplayNode%2BSubclasses.h) to implement node hierarchies or custom drawing. ### Learn more * Read the [Getting Started guide](http://asyncdisplaykit.org/guide/) * Get the [sample projects](https://github.com/facebook/AsyncDisplayKit/tree/master/examples) * Browse the [API reference](http://asyncdisplaykit.org/appledoc/) * Watch the [NSLondon talk](http://vimeo.com/103589245) ## Testing AsyncDisplayKit has extensive unit test coverage. You'll need to run `pod install` in the root AsyncDisplayKit directory to set up OCMock. ## Contributing See the CONTRIBUTING file for how to help out. ## License AsyncDisplayKit is BSD-licensed. We also provide an additional patent grant. The files in the /examples directory are licensed under a separate license as specified in each file; documentation is licensed CC-BY-4.0. --- Welcome to the AsyncDisplayKit beta! Documentation — including this README — will be fleshed out for the initial public release. Until then, please direct questions and feedback to the [Paper Engineering Community](https://www.facebook.com/groups/551597518288687) group. --- AsyncDisplayKit is a library for smooth asynchronous user interfaces on iOS. UIKit traditionally only runs on the main thread. This includes expensive tasks like text sizing and rendering, as well as image decoding. AsyncDisplayKit can handle all of the expensive parts asynchronously on a background thread, caching the values before actual views are created, and finally laying out views or layers efficiently on the main thread. AsyncDisplayKit uses display nodes to represent views and layers. The node API is designed to be as similar as possible to UIKit's. ## Install AsyncDisplayKit is available on [CocoaPods](http://cocoapods.org/). You can manually include it in your project's Podfile: pod 'AsyncDisplayKit', :git => 'git@github.com:facebook/AsyncDisplayKit.git' and run pod install ## Usage Start by importing the header: #import An _ASDisplayNode_ is an abstraction over _UIView_ and _CALayer_ that allows you to perform all standard view- and layer-related tasks (layout, sizing calculations, view hierarchy management), moving expensive work off the main thread and with additional optimisations. More specifically, with node-based hierarchy, you can: - offload more-complex layout code to the background - enable layer-backing (on nodes that don't require touch handling) - enable subtree precompositing - create entire view hierarchies over multiple runloops and do all of this in advance and in the background, so complex content is ready to go by the time the user scrolls to it. The node API is designed to be as similar as possible to UIView. _ASDisplayNode_ can be allocated, initialized and its properties can be set all on a background thread. dispatch_async(background_queue, ^{ n = [[ASDisplayNode alloc] init]; n.frame = CGRectMake(0, 40, 100, 100); n.backgroundColor = [UIColor greenColor]; }); Hierarchies can be created in a similar way as UIKit: dispatch_async(background_queue, ^{ s = [[ASDisplayNode alloc] init]; [n addSubnode:s]; }); A node may be backed by a view or layer, which must take place on the main thread. At this point, views may read their cached calculatedSize when performing layout. dispatch_async(dispatch_get_main_queue(), ^{ UIView *v = [node view]; // You can now add it to the view hierarchy [someView addSubview:v]; // The properties you set earlier will be preserved // v.frame is {0, 40, 100, 100} // v.backgroundColor is green }); Besides _ASDisplayNode_, AsyncDisplayKit has UIKit equivalent classes: - _ASControlNode_: a UIButton equivalent - _ASTextNode_: a UITextView equivalent, with features like tap highlights, custom truncation strings, gradients, shadows, and tappable links. - _ASImageNode_: a UIImageView equivalent - _ASTableView_: a UITableView subclass that uses nodes instead of cells ## Subclassing To make your own nodes, start with the subclasses header #import The [header](https://github.com/facebook/AsyncDisplayKit/blob/master/AsyncDisplayKit/ASDisplayNode%2BSubclasses.h) contains possible and recommanded methods to override. ## Documentation See the [wiki](https://github.com/facebook/AsyncDisplayKit/wiki) for more details on use cases, performance, subclassing, bridged properties, sizing and layout, and UIKit divergence. ## Testing AsyncDisplayKit has extensive unit test coverage. You'll need to run `pod install` in the root AsyncDisplayKit directory to set up OCMock. ## Contributing See the CONTRIBUTING file for how to help out. ## License AsyncDisplayKit is BSD-licensed. We also provide an additional patent grant. --- ## NS_ENUM 11 Jun 2012 Xcode 4.5 GM Seed 1和iOS 6的新特性