2016.05.20 10:24java
尘封已久的学习基础总结,最近公司项目不是很忙,终于抽空整理出来,现分享出来。node
一次性执行 dispatch_once
,延迟操做dispatch_after
(这里是延迟推到线程中,而不是在线程中等待,所以好比设置延迟1秒执行,可是一秒后只是推到了线程中,不会马上执行),调度组等,其高级功能有
dispatch_barrier_async
栅栏来控制异步操做的顺序dispatch_apply
充分利用多核进行快速迭代遍历dispatch_group_t
队列组,添加到队列组中的任务完成以后会调用dispatch_group_notify
函数,能够实现相似A、B两个耗时操做都完成以后,去主线程更新UI的操做maxConcurrentOperationCount
,继续/暂停/所有取消,能够快队列设置操做的依赖关系,经过KVO监听 NSOperation 对象的属性,如 isCancelled、isFinished;对象可重用。
NSInvocationOperation
和NSBlockOperation
建立方法等这些基础面试官每每默认你是会的NSOperationQueue
只有两种队列:主队列、其余队列。其余队列包含了串行和并发NSOperation + NSOperationQueue
将任务加入到队列[operation2 addDependency:operation1];
(operation2 依赖于operation1的完成,但这两个任务要加入到同一个队列中)一般耗时的操做都放在子线程处理,而后到主线程更新UI,如ios
这个就须要用到字典,以图片的下载地址url为key,下载操做为value,全部的图片大概分红三类:已经下载好的,正在下载的和将要下载的;面试
当一张图片将要进行下载操做的时候,先判断缓存中是否有相同的图片,若是有的话就返回,没有的话就根据url的md5加密值去沙盒中找,有的话就拿出来用,没有的话再去以图片的url为key去字典中找有没有正在进行的任务,最后去判断等待的下载操做任务里面的字典有无相同key,若是没有,就本身开启任务,记录一下,文件保存的名称是url的md5值spring
这里创建了两个字典 : 1.iconCache:保存缓存的图片 2.blockOperation 用来保存下载任务 数据库
每当进入或退出程序时,会进行图片文件的管理:超过一星期的文件会被清除,若是设置了最大缓存,超过这个缓存就会删除最旧的文件,直到当前缓存文件为最大缓存文件的一半大小;swift
通常app中大部分缓存都是图片的状况下,能够直接调用clear方法进行清除缓存,getSize()方法获取当前缓存大小。设计模式
如何处理事件数组
界面刷新: 当UI改变( Frame变化、 UIView/CALayer 的继承结构变化等)时,或手动调用了 UIView/CALayer 的 setNeedsLayout/setNeedsDisplay方法后,这个 UIView/CALayer 就被标记为待处理。 苹果注册了一个用来监听BeforeWaiting和Exit的Observer,在它的回调函数里会遍历全部待处理的 UIView/CAlayer 以执行实际的绘制和调整,并更新 UI 界面。浏览器
手势识别: 若是上一步的 _UIApplicationHandleEventQueue() 识别到是一个guesture手势,会调用Cancel方法将当前的touchesBegin/Move/End 系列回调打断。随后系统将对应的 UIGestureRecognizer 标记为待处理。 苹果注册了一个 Observer 监测 BeforeWaiting (Loop即将进入休眠) 事件,其回调函数为 _UIGestureRecognizerUpdateObserver(),其内部会获取全部刚被标记为待处理的 GestureRecognizer,并执行GestureRecognizer的回调。 当有 UIGestureRecognizer 的变化(建立/销毁/状态改变)时,这个回调都会进行相应处理。
网络请求:最底层是CFSocket层,而后是CFNetwork将其封装,而后是NSURLConnection对CFNetwork进行面向对象的封装。当网络开始传输时,NSURLConnection建立了两个新线程:com.apple.NSURLConnectionLoader和com.apple.CFSocket.private。其中CFSocket线程是处理底层socket链接的。NSURLConnectionLoader这个线程内部会使用RunLoop来接受底层socket的事件,并添加到上层的Delegate
应用
滑动与图片刷新:当tableView的cell上有须要从网络获取的图片的时候,滚动tableView,异步线程回去加载图片,加载完成后主线程会设置cell的图片,可是会形成卡顿。能够设置图片的任务在CFRunloopDefaultMode下进行,当滚动tableView的时候,Runloop切换到UITrackingRunLoopMode,不去设置图片,而是而是当中止的时候,再去设置图片。(在viewDidLoad中调用self.imageView performSelector@selector(setImage) withObject:...afterDelay:...inModes@[NSDefayltRunLoopMode])
常驻子线程,保持子线程一直处理事件 为了保证线程长期运转,能够在子线程中加入RunLoop,而且给Runloop设置item,防止Runloop自动退出
//1.得到NSUserDefaults文件
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
//2.向文件中写入内容
[userDefaults setObject:@"AAA" forKey:@"a"];
[userDefaults setBool:YES forKey:@"sex"];
[userDefaults setInteger:21 forKey:@"age"];
//2.1当即同步
[userDefaults synchronize];
//3.读取文件
NSString *name = [userDefaults objectForKey:@"a"];
BOOL sex = [userDefaults boolForKey:@"sex"];
NSInteger age = [userDefaults integerForKey:@"age"];
NSLog(@"%@, %d, %ld", name, sex, age);
复制代码
// 反归档
- (id)initWithCoder:(NSCoder *)aDecoder {
if ([super init]) {
self.avatar = [aDecoder decodeObjectForKey:@"avatar"];
self.name = [aDecoder decodeObjectForKey:@"name"];
self.age = [aDecoder decodeIntegerForKey:@"age"];
}
return self;
}
// 归档
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:self.avatar forKey:@"avatar"];
[aCoder encodeObject:self.name forKey:@"name"];
[aCoder encodeInteger:self.age forKey:@"age"];
}
复制代码
* 归档,把对象归档时须要调用NSKeyedArchiver的工厂方法archiveRootObject: toFile: 方法
复制代码
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [[Person alloc] init];
person.avatar = self.avatarView.image;
person.name = self.nameField.text;
person.age = [self.ageField.text integerValue];
[NSKeyedArchiver archiveRootObject:person toFile:file];
复制代码
* 反归档
复制代码
NSString *file = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject stringByAppendingPathComponent:@"person.data"];
Person *person = [NSKeyedUnarchiver unarchiveObjectWithFile:file];
if (person) {
self.avatarView.image = person.avatar;
self.nameField.text = person.name;
self.ageField.text = [NSString stringWithFormat:@"%ld", person.age];
}
复制代码
属性列表
数据库:SQLite
Core Data 点击查看大神讲解
属性列表
从存储数据大小来看,归档、偏好设置、属性列表三种方法适合存储数据量较小的数据,数据库、CoreData方法适合存储数据量较大的数据
从加密性来看,其中归档会将数据进行加密,而偏好设置是直接保存到属性列表中,不会对数据进行加密
从存储类型来看,属性列表只能存放固定的七种类型(可在plist文件中看到),归档对存储类型无限制
KVC(key-value-coding键值编码,跟多状况下会简化程序代码)的常见用法:
KVC缺点:一旦使用KVC,编译器没法检查出错误,即不会对设置的键、键路径进行错误检查,且执行效率低于自定义的setter和getter方法,由于使用KVC键值编值,必须先解析字符串,而后设置或访问对象的实例变量
经过KVO(key-value-observing,典型的观察者模式,被观察的对象必须使用KVC键值编码来修改它的实例变量,这样才能被观察者观察到)监听person对象中name属性发生改变
[self.person addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
复制代码
* 当person的name的值发生改变时,就会执行该方法
复制代码
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
do something....
}
复制代码
GET请求的数据会负载URL以后,即把数据放在HTTP协议头中,以?区分URL和传输数据,参数之间以&相连,英文字母/数字,原样发送,若是是空格,转化为+,若是是中文,把字符串用BASE64加密;POST就是把提交的数据放在HTTP包的包体中
GET通常用于提交少许数据(最多提交1k,浏览器限制),POST用于提交大量数据(理论上无限制,收服务器限制)
GET 无反作用,POST 有反作用
GET提交的数据能够在浏览器历史记录中看到,安全性很差,别人能够拿到帐号密码,POST不会
Get是向服务器发索取数据的一种请求,而POST是向服务器发提交数据的一种请求,只是发送机制不一样
GET不能够设置书签,POST能够设置书签
POST支持更多编码类型且不对数据类型限制
什么状况下用POST:
什么状况下用GET:
POST请求的url表示处理该封闭实体的资源,该资源多是个数据接收过程、某种协议的网关、或者接收注解的独立实体。
PUT请求中的url表示请求中封闭的实体-用户代理知道url的目标,而且服务器没法将请求应用到其余资源。若是服务器但愿该请求应用到另外一个url,就必须发送一个301响应;用户代理可经过本身的判断来决定是否转发该请求。
POST是用来提交数据的。提交的数据放在HTTP请求的正文里,目的在于提交数据并用于服务器端的存储,而不容许用户过多的更改相应数据(主要是相对于在url 修改要麻烦不少)。
PUT操做是幂等的。所谓幂等是指无论进行多少次操做,结果都同样。好比我用PUT修改一篇文章,而后在作一样的操做,每次操做后的结果并无不一样
POST操做既不是安全的,也不是幂等的,好比常见的POST重复加载问题:当咱们屡次发出一样的POST请求后,其结果是建立出了若干的资源。
安全和幂等的意义在于:当操做没有达到预期的目标时,咱们能够不停的重试,而不会对资源产生反作用。从这个意义上说,POST操做每每是有害的,但不少时候咱们仍是不得不使用它。
还有一点须要注意的就是,建立操做可使用POST,也可使用PUT,区别在于POST 是做用在一个集合资源之上的,而PUT操做是做用在集合的一个具体资源之上的,再通俗点说,若是URL能够在客户端肯定,那么就使用PUT,若是是在服务端肯定,那么就使用POST,好比说不少资源使用数据库自增主键做为标识信息,而建立的资源的标识信息究竟是什么只能由服务端提供,这个时候就必须使用POST。
实际上是个面试官八个不懂Swift,并且通常不懂得就问个Swift与OC的不一样。。题主也只是在自学Swift,等到3.0出了以后再深刻研究,并且项目中可能也要开始从混编逐渐向Swift靠拢了。
Swift是一门更加现代化的语言,可是目前还在成长阶段,更新改动比较大,虽说其底层思想不变,变的是API和接口,可是每次更新完Xcode看到本身的Swift项目仍是有些淡淡的忧伤,并且目前Swift开发都要转成OC的runtime,包略大,所以题主认为成熟项目最好仍是采用OC
先记住一句话:OC底层面向对象,而Swift底层更加面向协议
咱们已经见识过Apple使用了大量协议,好比在tableView当中,咱们能够经过协议来告诉Apple须要多少个表视图单元格,而不是每时每刻都要继承UITableViewController
在这里以MVVM做为测试用例:好比如今须要创建一个相似设置界面的tableView,每一个cell须要一个label和一个switch,自定义SwitchWithTextTableViewCell,在其内部创建一个configure方法中对label的title,titleFont,titleColor,switch的switchOn和switchColor等进行初始化,但这种方式很是累赘,好比添加一个副标题,就须要额外添加三个属性
可是利用协议SwitchWithTextCellProtocol,让视图模型实现这个协议,而后在这里设置全部的属性
protocol SwitchWithTextCellProtocol {
var title: String { get }
var titleFont: UIFont { get }
var titleColor: UIColor { get }
var switchOn: Bool { get }
var switchColor: UIColor { get }
func onSwitchTogglenOn(onL Bool)
}
复制代码
经过swift2.0重点饿协议扩展,就能够经过默认值来作一些处理了,若是对于大多数单元格来讲,能够肯定某一种颜色的话,就能够对其创建扩展,而后设置颜色便可,全部实现此协议的视图就没有必要再去设置这个颜色了
如今,个人configure方法里面只要实现此协议的值就能够了
// 这个方法只须要一个参数,相比于以前的多个参数简便了不少
class SwitchWithTextTableViewCell: UITableViewCell {
func configure(withDelegate delagate: SwitchWithTextCellProtocol) {
// 在这里配置方法
}
}
复制代码
struct MinionModeViewController: SwitchWithTextCellProtocol {
var title = "excellent!!"
var switchOn = true
var switchColor: UIColor {
return .yellowColor()
}
func onSwitchToggleOn(on: Bool) {
if on {
print("The Minions are here to stay!")
} else {
print("The Minions went out to play!")
}
}
}
复制代码
let cell = tableView.dequeueReuseableCellWithIdentifier("SwitchWithTextTableViewCell", forIndexPath: indexPath) as! SwitchWithTextTableViewCell
cell.configure(withDelegate: MinionModeViewModel())
return cell
复制代码
再把模型放在视图模型层级,一遍对其进行跟踪,再视图模型中传递这些信息,这样单元格就能够生成了
protocol SwitchWithTextCellDataSource {
var title: String { get }
var switchOn: Bool { get }
}
protocol SwitchWithTextCellDelegate {
func onSwitchTogglenOn(on: Bool)
var switchColor: UIColor { get }
var textColor: UIColor { get }
var font: UIFont { get }
}
复制代码
// SwitchWithTextTableViewCell
func configure(withDataSource dataSource: SwitchWithTextCellDataSource, delegate: SwitchWithTextCellDelegate?) {
// 在这里配置视图
}
复制代码
struct MinionModeViewModel: SwiftWithTextCellDataSource {
var title = "Minion Mode!!"
var switchOn = true
}
复制代码
extension MinionModeViewModel: SwitchWithTextCellDelegate {
var switchColor: UIColor {
return .yellowColor()
}
func onSwitchToggleOn(on: Bool) {
if on {
print("The Minions are here to stay!")
} else {
print("The Minions went out to play!")
}
}
}
复制代码
// SettingViewController
let viewModel = MinionModeViewModel()
cell.configure(withDataSource:viewModel, delegate: viewModel)
return cell
复制代码
仅仅须要建立视图模型,而后将其传递到配置方法当中,最后返回单元格,就能够了
在游戏开发中一般会有一个很庞大的层级关系,以及一系列的继承,好比各类怪,继承在这里显得十分有意义,可是随着层级的扩展,这个项目就会变得凌乱起来
好比说须要设计一个能够射击的怪物,但这时候塔防顶部的大炮也会射击,就须要把“射击辅助类”提取出来,可是若是一直这样提取子类,代码后面会一团乱麻
将这个代码重构,再也不去提取可以射击或者可以加血的子类,而是将其提取为协议,经过协议扩展来实现这个功能,代码更加简洁,更利于理解
// 一看这个对象的类型,就知道他有哪些功能,而不是一个个去查找她的实现
class ZapMonster: GameObject, GunTraint, HealthTraint, MovementTraint {
...
}
复制代码
protocol TextPresentable {
var text: String { get }
var textColor: UIColor { get }
var font: UIFont { get }
}
protocol SwitchPresentable {
var switchOn: Bool { get }
var switchColor: UIColor { get }
func onSwitchToggleOn(on: Bool)
}
复制代码
这种状况下,好比须要一个图片框,只要一个iamgeProtocol就能够了,设计师要求改全部标签的颜色的话一行代码就能够搞定
class SwitchWithTextTableViewCell<T where T: TextPresentable, T: SwitchPresentable>: UITableViewCell {
private var delegate: T?
// T是视图模型
func configure(withDelegate delegate: T) {
// 在这里配置视图
}
}
复制代码
在这种状况下,它没有实现这些协议,可是会期待某种实现这些协议的东西传递进去,所以咱们使用了泛型,这个单元格期待了一个实现了TextPresentableProtocol 的委托。就咱们而言,传递进去的将是一个实现了这些协议的东西就能够了,如今要基于这些信息在单元格当中配置全部的东西了,如今就能够基于浙西而信息在单元格中配置全部的东西了
extension MinionModeViewModel: TextPresentable {
var text: String { return "Minion Mode" }
var textColor: UIColor { return .blackColor() }
var font: UIFont { return .systemFontOfsize(17.0) }
}
复制代码
咱们的视图模型将会有一个TextPresentable代码,在其中能够配置文本、颜色、字体,而且因为全部的这些协议扩展中都已经有默认值了,甚至不须要视图模型去实现这些具体的内容
最后,视图模型当中的代码就只须要dequeue相应的单元格。而后经过视图模型对其进行配置,而后返回单元格便可
一种是针对全部 Cell 具备固定高度的状况,经过:self.tableView.rowHeight = 88; 指定了一个全部 cell 都是 88 高度的 UITableView,对于定高需求的表格,强烈建议使用这种(而非下面的)方式保证没必要要的高度计算和调用。
另外一种方式就是实现 UITableViewDelegate 中的:heightForRowAtIndexPath:须要注意的是,实现了这个方法后,rowHeight 的设置将无效。因此,这个方法适用于具备多种 cell 高度的 UITableView。
iOS7以后出了了estimatedRowHeight,面对不一样高度的cell,只要给一个预估的值就能够了,先给一个预估值,而后边滑动边计算,可是缺点就是
iOS8 WWDC 中推出了 self-sizing cell 的概念,旨在让 cell 本身负责本身的高度计算,使用 frame layout 和 auto layout 均可以享受到:
相同的代码在 iOS7 和 iOS8 上滑动顺畅程度彻底不一样,iOS8 莫名奇妙的卡。很大一部分缘由是 iOS8 上的算高机制大不相同,从 WWDC 也却是能找到点解释,cell 被认为随时均可能改变高度(如从设置中调整动态字体大小),因此每次滑动出来后都要从新计算高度。
使用 UITableView+FDTemplateLayoutCell(百度知道负责人孙源) 无疑是解决算高问题的最佳实践之一,既有 iOS8 self-sizing 功能简单的 API,又能够达到 iOS7 流畅的滑动效果,还保持了最低支持 iOS6
FDTemplateLayoutCell 的高度预缓存是一个优化功能,利用RunLoop空闲时间执行预缓存任务计算,当用户正在滑动列表时显然不该该执行计算任务影响滑动体验。
iOS平台由于UIKit自己的特性,须要将全部的UI操做都放在主线程执行,因此有时候就习惯将一些线程安全性不肯定的逻辑,以及它线程结束后的汇总工做等等放到了主线程,因此主线程包含大量计算、IO、绘制都有可能形成卡顿。
设置正确的 reuseidentifer 以重用 cell
尽可能将 View 设置为不透明,包括 cell 自己(backgroundcolor默认是透明的),图层混合靠GPU去渲染,若是透明度设置为100%,那么GPU就会忽略下面全部的layer,节约了不少没必要要的运算。模拟器上点击“Debug”菜单,而后选择“color Blended Layers”,会把全部区域分红绿色和红色,绿色的好,红色的性能差(通过混合渲染的),固然也有一些图片虽然是不透明的,可是也会显示红色,若是检查代码没错的话,通常就是图片自身的性质问题了,直接联系美工或后台解决就行了。除非必需要用GPU加载的,其余最好要用CPU加载,由于CPU通常不会百分百加载,能够经过CoreGraphics画出圆角
有时候美工失误,图片大小给错了,引发没必要要的图片缩放(能够找美工去改,固然也能够异步去裁剪图片而后缓存下来),仍是使用Instrument的Color Misaligned Images,黄色表示图片须要缩放,紫色表示没有像素对齐。固然通常状况下图片格式不会给错,有些图片格式是GPU不支持的,就还要劳烦CPU去进行格式转换。还有能够经过Color Offscreen-Rendered Yellow来检测离屏渲染(就是把渲染结果临时保存,等到用的时候再取出,这样相对于普通渲染更消耗内存,使用maskToBounds、设置shadow,重写drawRect方法都会致使离屏渲染) 避免渐变,cornerRadius在默认状况下,这个属性只会影响视图的背景颜色和 border,可是不会离屏绘制,不影响性能。不用clipsToBounds(过多调用GPU去离屏渲染),而是让后台加载图片并处理圆角,并将处理过的图片赋值给UIImageView。UIImageView 的圆角经过直接截取图片实现,圆角路径直接用贝塞尔曲线UIBezierPath绘制(人为指定路径以后就不会触发离屏渲染),UIGraphicsBeginImageContextWithOptions。UIView的圆角可使用CoreGraphics画出圆角矩形,核心是CGContextAddArcToPoint 函数。它中间的四个参数表示曲线的起点和终点坐标,最后一个参数表示半径。调用了四次函数后,就能够画出圆角矩形。最后再从当前的绘图上下文中获取图片并返回,最后把这个图片插入到视图层级的底部。 “Flash updated Regions”用于标记发生重绘的区域
若是 row 的高度不相同,那么将其缓存下来
若是 cell 显示的内容来自网络,那么确保这些内容是经过异步下载
使用 shadowPath 来设置阴影,图层最好不要使用阴影,阴影会致使离屏渲染(在进入屏幕渲染以前,还看不到的时候会再渲染一次,尽可能不要产生离屏渲染)
减小 subview 的数量,不要去添加或移除view,要就显示,不要就隐藏
在 cellForRowAtIndexPath 中尽可能作更少的操做,最好是在别的地方算好,这个方法里只作数据的显示,若是须要作一些处理,那么最好作一次以后将结果储存起来.
使用适当的数据结构来保存须要的信息,不一样的结构会带来不一样的操做代价
使用,rowHeight , sectionFooterHeight 和 sectionHeaderHeight 来设置一个恒定高度 , 而不是从代理(delegate)中获取
cell作数据绑定的时候,最好在willDisPlayCell里面进行,其余操做在cellForRowAtIndexPath,由于前者是第一页有多少条就执行多少次,后者是第一次加载有多少个cell就执行多少次,并且调用后者的时候cell还没显示
读取文件,写入文件,最好是放到子线程,或先读取好,在让tableView去显示
tableView滚动的时候,不要去作动画(微信的聊天界面作的就很好,在滚动的时候,动态图就不让他动,滚动中止的时候才动,否则可能会有点影响流畅度)。在滚动的时候加载图片,中止拖拽后在减速过程当中不加载图片,减速中止后加载可见范围内图片
重用问题:好比UITableViewCell、UICollectionViewCell、UITableViewHeaderFooterViews等设置正确的reuseIdentifier,充分重用
懒加载控件、页面:对于不是马上使用的数据,都应该使用延迟加载的方式,好比网络链接失败的提示界面,可能一直都用不到
使用Autorelease Pool:在某些循环建立临时变量处理数据时,自动释放池以保证能及时释放内存
不要使用太多的xib/storyboard:载入时会将其内部的图片在内的全部资源载入内存,即便将来好久才会须要使用,相对于纯代码写的延迟加载,在性能和内存上就差了不少
数据缓存:对于cell的行高要缓存起来,使用reloadData效率也极高,对于网络数据,不须要每次都请求的,应该缓存起来,能够写入数据库,也能够经过plist文件存储
选择正确的数据结构:针对不一样的业务场景选择最合适的数据结构是写出高效代码的基础
gzip/zip压缩:当从服务器下载相关附件时,能够经过 zip压缩后再下载,使得内存更小,下载速度也更快
重大开销对象:一些objects的初始化很慢,好比NSDateFormatter和 NSCalendar,可是又无可避免的须要使用,一般做为属性存储起来,避免反复使用
避免反复处理数据:须要应用须要从服务器加载数据,常为JSON或者XML格式的数据,在服务器端或者客户端使用相同的数据结构很重要
选择图片时,要对图片进行压缩处理,根据不一样的状况选择不一样的图片加载方式,-imageNamed:读取到内存后会缓存下来,适合图片资源较小,使用很频繁的图片;-initWithContentsOfFiles:仅加载图片而不缓存,适合较大的图片。如果collectionView中使用大量图片的时候,能够用UIVIew.layer.contents=(__bridge id _Nullable)(model.clipedImage.CGImage);这样就更轻量级一些
固然有时候也会用到一些第三方,好比在使用UICollectionView和UITableView的时候,Facebook有一个框架叫AsyncDisplayKit,这个库就能够很好地提高滚动时流畅性以及图片异步下载功能(不支持sb和autoLayout,须要手动进行约束设置),AsyncDisplayKit用相关node类,替换了UIView和它的子类,并且是线程安全的。它能够异步解码图片,调整图片大小以及对图片和文本进行渲染,把这些操做都放到子线程,滑动的时候就流畅许多。我认为这个库最方便的就是实现图片异步解码。UIImage显示以前必需要先解码完成,并且解码仍是同步的。尤为是在UICollectionView/UITableView 中使用 prototype cell显示大图,UIImage的同步解码在滚动的时候会有明显的卡顿。另一个很吸引人的点是AsyncDisplayKit能够把view层次结构转成layer。由于复杂的view层次结构开销很大,若是不须要view特有的功能(例如点击事件),就可使用AsyncDisplayKit 的layer backing特性从而得到一些额外的提高。固然这个库还处于开发阶段,还有一些地方地方有待完善,好比不支持缓存,我要使用这个库的时候通常是结合Alamofire和AlamofireImage实现图片的缓存
但愿此文对您的求职或夯实基础起到做用,感谢阅读!