WWDC 2018:Cocoa Touch新特性与改进

今年的Session 202 - What's New in Cocoa Touch是这样的逻辑:objective-c

  • UIKit旧有功能的性能优化
    • 滚动新增数据预获取功能
    • 下降图片渲染没必要要的内存开销
    • 优化Auto Layout计算的时间复杂度
  • UIKit旧有功能的API调整
    • 框架中旧的全局变量、常量以及函数被嵌套进对象,使得它们看上去更Swift
    • 淘汰旧版本的编解码API,新增更安全的编解码API
  • UIKit的新功能
    • Notification的交互、分组、设置
    • Messages的动态贴纸、iPhone X下Home Bar的交互响应
    • 密码的自动生成和填充
    • Siri Shortcut

UIKit的性能提高


  • UIKit经过“数据预加载”和“CPU算力调整”来作了滚动优化,这两项优化对于UITableView和UICollectionView甚至你本身定制的UIScrollView都有效。
  • UIKit针对图片渲染相关API的优化了内存开销
  • UIKit优化了Auto Layout运算的开销

“数据预加载”的背后是一个这样的故事:

咱们举UITabelView为例子,一次完整的Cell加载过程是这样的:算法

  1. 在UITableView的DataSource的- tableView:cellForRowAt:方法中,工程师将Cell实例从缓存队列中取出,或直接初始化一个新cell
  2. 工程师将数据取出,而后赋值给Cell实例
  3. UIKit会调用layoutSubviews给cell的子view做布局
  4. UIKit将cell绘制出来并展现

只有一帧以内作完这4件事情,UITableView滚动的时候才不会卡。数据库

在以上4步中,第2步开销大小是取决于不一样场合不一样业务逻辑的。由于有的时候你用于展现这个Cell的数据可能已经预先整理过,直接拿来就可用,不会有什么大的开销。但有的时候你用于展现这个Cell的数据是来自于数据库、或者本地文件、或者其它地方的,那么就会多一个从其余地方取数据整理数据的过程,那么就会增长一笔额外开销,这一笔开销就颇有可能致使掉帧卡顿。swift

如上图所示,在以前UIKit的实现中,预先取数据这个行为自己也是有的,并且也是异步的。但因为取数据的时机是跟加载Cell的时机是同时开始的,因此CPU又要预先取数据,也要加载、渲染Cell。这就致使了CPU在一帧的时间内同时作两件事,就有可能致使掉帧。缓存

而此次的优化,就是把预先取数据的行为放在了Cell渲染完成以后。这样就能够在一帧刚开始的时候,将CPU从数据预加载的事务中解放出来,全力去作Cell相关的事情,这样就可以尽早将Cell渲染完交差。而后CPU再进行数据异步预加载的事情。安全

在这一轮优化中,UIKit针对数据预加载提供了新的Protocol。拿UITableView来讲,就提供了UITableViewDataSourcePrefetching,这个Protocol中包含两个方法:性能优化

func tableView(_ tableView: UITableView, prefetchRowsAt indexPaths: [IndexPath])
func tableView(_ tableView: UITableView, cancelPrefetchingForRowsAt: indexPaths [IndexPath])
复制代码
- (void)tableView:prefetchRowsAt:
- (void)tableview:cancelPrefetchingForRowsAt:
复制代码

实现这个protocol时,只有prefetchRowsAt这个方法是必须的。cancelPrefetchingForRowsAt这个会由系统调用,好比当你cell的数据是须要下载才能获得的,那么结合断点续传功能和这个protocol,你就能很优雅地处理cell数据加载的事情。markdown


“算力调整”的背后是这样的故事:

苹果工程师在不断地作性能检测的时候,又发现了一个这样的状况:若是用户以前在看一些加载开销比较小的Cell,而后用户忽然滚动到加载开销比较大的Cell时,UIKit依旧会出现掉帧的卡顿现象。并且此时系统自己也没什么负载,也没有什么开销大的后台任务在运行,所以就排除了CPU负载过大致使卡顿这一可能。app

那为何CPU没什么负载,却依旧出现了卡顿呢?他们研究了一轮,发现原来这是由于CPU性能调度逻辑致使的,这段过程是这样:框架

  1. 当用户以前一直在看加载开销小的Cell的时候,其自己须要的CPU性能并不高,那么此时CPU性能调度管理器就更加倾向于保持低性能运行,这样可以省电。
  2. 当用户忽然划到一个加载开销大的Cell的时候,CPU性能调度管理器也并不知道你这时候要加载一个开销那么大的Cell,因而它就仍是在低性能的状态下去加载这个开销比较大的Cell。
  3. 当CPU性能调度器反应过来这个Cell原来须要那么大的开销的时候,它就会切到高性能去消化这个负载。不过每每这时候再提升CPU性能就已经晚了,就有很大概率会致使卡顿。

这个过程每每会发生在Feed流相似的应用中。好比你以前一直在看的Cell都是文字内容的,开销极小。忽然你划到视频Cell了,这个cell开销很大,CPU性能调度管理器在Cell加载到一半了才去提升性能等级,那就很容易出现卡顿的状况了。

找到缘由以后,优化的事情就好作了。如今苹果工程师打通了整个流程,使得UIKit的信息可以传递到CPU性能调度管理器。当UIKit在加载下一个Cell的时候,若是发现这是一个加载开销比较大的Cell,UIKit就会去通知CPU性能调度器,告诉调度器赶忙提升性能。这样一来,由于CPU性能已经提早提上来了,那么系统就能最大可能避免掉帧卡顿的状况发生。

也就是说,以前CPU的调度状况是这样:

优化以后,CPU的调度就变成了这样:

由于UIKit预测到了下一个Cell开销比较大,因此提早跟CPU性能调度器打了招呼,因而CPU的性能就马上提上来了。相比以前CPU性能慢慢往上提的状况,就更不容易出现卡顿。


图片渲染相关API的内存优化

演讲者先说了一遍内存是如何影响UI性能的,这一段其实能够说得很简单:当系统剩余内存很少,而此时你的应用又须要很大的一片内存时,系统就要经过回收内存或强行关闭后台App的方式来为你的应用腾出内存来。系统为你腾内存这一操做的开销致使了你的App此时须要更长时间的等待,进而影响了你App的UI性能。

为了缓解这个问题,iOS 12引入了Automatic Backing Store这项技术。这项技术主要是针对画图类的App起做用,对于其余的场合无效。本质上的实现就是经过在保证色彩不失真的基础上,使用更少的数据量,去表达一个像素的颜色。

以下图所示,在64位色的状况下,左右两张图片的数据量是同样的,都是2.2M左右。虽然右边只有黑白两色,可是依旧使用了64位去存储每一个像素的颜色色值。

因此呢,苹果就想了个办法,说既然右边这张图片只有黑白两色,那我干吗还要用64位去表达一个颜色呢?8位就足够了啊。因而,右边这个图每像素的颜色用8位来表达以后,就变成只有275K了,因而内存就省了:

那为何不直接用1位呢?反正也就黑白两色。对不起,由于色表标准最小就是8位的,若是你用了1位色,屏幕是不支持这套标准的。Automatic Backing Store对左边这幅图无效,由于它不是图片压缩算法,它只是在你用画笔画完图片而且渲染的时候,把原来须要64位去存储的单个像素的色值,改为用8位去存储而已,这个过程是有可能产生必定的色彩失真的。因此Automatic Backing Store只适合画图类应用,在如下3个场景中,它是默认开启的:

  • UIView.draw()
  • UIGraphicsImageRenderer
  • UIGraphicsImageRenderer.Range

有关Automatic Backing Store的内容,还能够看Session 219 - Image and Graphics Best Practices


Auto Layout计算的优化

iOS 12中,Auto Layout作了很是大的优化。

当一个View包含了N个布局上互相独立的子View时,Auto Layout的计算耗时是随着子view数量的增长而线性增长的。由于苹果工程师们的努力,iOS 12的计算耗时比iOS 11少了一些,以下图:

图A

当一个View包含了N个子View,且这N个子View的布局存在依赖的状况时。iOS 11下Auto Layout的计算耗时是随着子View的数量增长而指数性增加的。在iOS 12优化以后,计算耗时呈线性增加,这能够说是一个比较大的优化了。(但我一直是认为其实这自己就应该是一个线性时间内能够完成的任务,iOS 11的时候工程师怎么把它实现成指数性增加的了?这个工程师该杀),以下图:

当N个View相互嵌套时,iOS 11下Auto Layout的计算耗时是随着子View的数量增长而指数性增加的。在iOS 12优化以后,计算耗时呈线性增加。我仍是以为这自己也是一个线性时间内能够完成的任务,当初在iOS 11把它实现成指数性增加任务的工程师该杀。以下图:

更多的内容能够看Session 220 - High Performance Auto Layout


UIKit旧有API的修改


  • 更Swift的UIKit
  • 更安全的编解码

更Swift的UIKit

在Swift 4.2中,有些全局的Type被挪进了对象中,包括但不限于:

还有些全局常量也被挪进了有关对象中,包括但不限于:

还有些全局函数也被挪近了有关对象中,包括但不限于:

这么一来,UIKit看起来确实更Swift了,只是升级4.2的时候,又要面对一堆编译错误了。


更安全的编解码

演讲者只是在这里提了一句,iOS 12提供了更安全的编解码API,旧的API将会被淘汰。具体是什么状况要去看Session 222 - Data You Can Trust


UIKit 新功能


  • Notification
  • Messages
  • Automatic Passwords and Security Code AutoFill
  • Siri Shortcuts

Notification API修改

  • Interaction

    • iOS 12增强了针对Notification的交互,大部分界面开发者能够自由定制了。
  • Grouping

    • iOS 12会将众多Notification分组并展现,而且提供了API让开发者在必定程度上决定如何进行分组。
  • Settings

    • iOS 12向开发者提供了接口,使得开发者可以让用户直接开启或关闭通知。

以上三点演讲者只是一带而过,更多内容请看:

Session 710 - What's New in User Notifications

Session 711 - Using Grouped Notifications


Messages

iOS 12中,你能够将Message的人脸识别贴纸带入camera中。若是你须要针对这些作更多的定制的话,你须要在Info.Plist中添加如下内容,MSMessagesAppPresentationContextMedia其实就是指的camera:

<key>MSSupportedPresentationContexts</key>
<array>
    <string>MSMessagesAppPresentationContextMessages</string>
    <string>MSMessagesAppPresentationContextMedia</string>
</array>
复制代码

并且你也能够根据如下API查看一条Message是否是动态的:

var presentationContext: MSMessagesAppPresentationContext

enum MSMessagesAppPresentationContext : UInt {
 case messages
 case media
}
复制代码

iOS 12也给Message添加了新的交互操做:以前在底部bar处水平滑动,系统会自动切换App。如今你的Message App能够接收这个事件,能够选择覆盖系统切换App的行为,或者执行你本身的行为。


Automatic Passwords and Security Code AutoFill

以前的iOS版本中,已经实现了自动输入密码的功能:

在iOS 12中,工程师能够在修改密码的地方也弹出提示,告知用户是否须要将密码保存在iCloud Keychain:

iOS 12还可以帮你自动生成密码,在注册流程和修改密码流程都能用,只要开发者给Password的输入框打上tag告知这是一个密码输入框就行了:

在输入手机号,发送验证码,输入验证码流程中,iOS 12在键盘上提供了读取验证码的功能,这样可以使得用户没必要切来切去:

以上这些内容演讲者也都只是稍微提了一下,具体内容能够看这里:Session 204 - Automatic Strong Passwords and Security Code AutoFill


Safe Area

演讲者把以前就已经有的SafeArea Insets讲了一遍,没有提到任何新内容。只是说但愿你们可以尽早使用Safe Area Insets去作不一样设备的UI适配。

最后给到了一个Session:Session 235 - UIKit: Apps for Every Size and Shape,根据演讲者的说法来看,这个Session里面可能有新内容,你们如果有兴趣的话能够看一看。


Siri Shortcuts

Siri Shortcurs可以使得你的App响应Siri的调度。具体的作法是响应来自各个App的Intents。固然,你也能够给本身的App设定Intents。每个Intents都有不一样的类型:

Siri会根据Intent找到对应的App,而后再解析用户给出的指令,来决定本次操做是哪一种类型,进而开发者给到对应的响应操做。

具体的状况仍是要看如下这些Session:

Session 211 - Introduction to Siri Shortcuts

Session 214 - Building for Voice with Siri Shortcuts

Session 217 - Siri Shortcuts on the Siri Watch Face


以上就是WWDC 2018 - Session 202 - What's New in Cocoa Touch的解说