iOS 9 by Tutorials 笔记(四)

Chapter 4:App Thinning

Apple 已经推出了很多种尺寸的 iPhone 了,从 iOS 方面,苹果也一直努力地消除这种硬件多元化给开发者带来的困扰,如 iOS 8 推出了 Adaptive Layout, Trait Collections, Universal split view controllers,从此你不再需要为某个特定的设备开发独占 App,而是开发一个通用 App 适配所有设备。

然而这也带来一些挑战,因为要包含所有设备的资源文件,这个通用的 App 所占空间容量都比较大。所以 iOS 9 拿出了新的解决方案:

  • App Slicing 当你将 iOS 9 打包的二进制文件提交到 App Store,Apple 会编译然后为每个特定的设备单独形成一个可执行文件,这样设备真正从商店下载的时候,只会根据特定的设备来下载安装,削减了尺寸。
  • On Demand Resources 应用资源可以在仅被需要时才下载
  • Bitcode bitcode是被编译程序的一种中间形式的代码。包含bitcode配置的程序将会在App store上被编译和链接。bitcode允许苹果在后期重新优化我们程序的二进制文件,而不需要我们重新提交一个新的版本到App store上。

以上这三种技术加起来被称为 App Thinning

Measuring your work

我们可以通过这种方式来对 App 瘦身过程进行量化

Slicing up app slicing

App slicing 可分为两部分:executable slicingresource slicingExecutable slicing 就是 Apple 将根据不同的设备安装特定的 App,你不需要做太多事情,Apple 已经为你做好了一切。

Being smart with resources

Resource slicing 要求你做的事情也很简单,将所有的资源文件放到 Asset Catalogs 下,并且按照相关特性进行组织,从 Xcode 7 开始,你可以根据 Memory 和 Graphics 来标记资源文件了

Lazily (down)loading content

现在我们通过对资源的『按需使用』来削减内容尺寸(俗称 ODR),ODR 允许你将资源存储在 Apple 的服务器上,之后仅在需要时才会下载使用。

NSBundleResourceRequest 负责处理 ODR,通过这个类,可以通过 Tags 来控制内容的下载。对 tags 的使用,Apple 模糊了本地资源和远程资源的界限。

使用 ODR 可以包括 images,data,OpenGL shaders,SpriteKit Particles,Watchkit Complications 等

针对 NSBundles 可看做是 data 文件,因此对于 ODR 来说也是支持的。

Wire things up to use tags

现在是代码时间,将之前的本地载入资源文件( NSBundles 文件)改为远程异步载入,修改 downloadAndDisplayMapOverlay() 方法

// 1
guard let bundleTitle =  
  mapOverlayData?.bundleTitle else {
return  
}
// 2
let bundleResource =  
NSBundleResourceRequest(tags: [bundleTitle])  
// 3
bundleResource.beginAccessingResourcesWithCompletionHandler {  
  [weak self] error in
// 4
  NSOperationQueue.mainQueue().addOperationWithBlock({
// 5
    if error == nil {
      self?.displayOverlayFromBundle(bundleResource.bundle)
    }
  }) 
}

beginAccessingResourcesWithCompletionHandler(_:) 会在完成 on-demand 内容下载后,调用 completion block 将资源文件显示到屏幕上。

How about those tags

现在我们完成最后一步:标记 tags

  1. 选中我们要标记的资源文件(LA_Map.bundle)
  2. 在 File Inspector 中找到 On Demand Resource Tags 部分
  3. 填写相应的 Tags

现在运行程序,选中 LA(LA_Map.bundle 被标记过,所以会被存储在云端),此时将从 Apple 的服务器上下载相应的资源文件

Make it download faster

LA 这个 bundle 相对来说还比较小,如果你尝试下 San Diego,会发现花很长时间来下载

对于已经通过 ODR 加载过的资源文件,再次显示的时候,ODR 会缓存来保证速度,除非触发了清空条件,否则将会一直缓存这些资源文件

为了避免应用被评为一星,你可以加一个进度条来告诉用户你的 app 正在下载内容,同样是回到 downloadAndDisplayMapOverlay() 方法

guard let bundleTitle =  
  mapOverlayData?.bundleTitle else {
  return
}
let bundleResource  
  = NSBundleResourceRequest(tags: [bundleTitle])
// 1
bundleResource.loadingPriority  
  = NSBundleResourceRequestLoadingPriorityUrgent
// 2
loadingProgressView.observedProgress  
  = bundleResource.progress
// 3
loadingProgressView.hidden = false  
UIApplication.sharedApplication()  
  .networkActivityIndicatorVisible = true

bundleResource.beginAccessingResourcesWithCompletionHandler {  
  [weak self] error in
  NSOperationQueue.mainQueue().addOperationWithBlock({
// 4
    self?.loadingProgressView.hidden = true
    UIApplication.sharedApplication()
      .networkActivityIndicatorVisible = false
    if error == nil {
      self?.displayOverlayFromBundle(bundleResource.bundle)
    }
  }) 
}

loadingProgressView 将会随下载进度进行实时更新

The many flavors of tagging

在真实世界,虽然你加上了进度条来做标记,但用户等待太长时间总归是件非常蛋疼的事情。因此你可以考虑将一些较大的资源文件设为初始数据打包进 IPA ,让他们一起下载安装

Initial install tags

选择工程名称 -> Target -> Resource Tags,将 All 切换为 Prefetched,会发现 ODR 有三种类型的处理方式:

  • Initial Install Tags:这个是资源文件随应用一起打包安装时使用,之后不需要时可以移除,所以也要由 ODR 来管理
  • Prefetched Tag Order:一旦完成下载,按顺序排列
  • Download Only On Demand:按需下载

现在我们可以根据需要,添加相应的资源文件

想要测试实际效果,需要提交到 TestFlight Beta Testing,然后用真实设备下载测试即可。

Purging content

你可以帮助 iOS 系统在资源文件不再需要时从磁盘清除掉

Set a resource to be purged

  1. 设置一个 NSBundleResourceRequest 类型的属性 overlayBundleResource 用来标记下载到的资源文件

    var overlayBundleResource: NSBundleResourceRequest?
    
  2. 将这个属性标记的指针指向获取到的资源文件,在资源文件下载显示方法 downloadAndDisplayMapOverlay() 中添加一条:

    overlayBundleResource = bundleResource
    
  3. 最后在屏幕消失时告诉系统完成了对资源文件的访问

    override func viewDidDisappear(animated: Bool) {
        super.viewDidDisappear(animated)
        overlayBundleResource?.endAccessingResources()
    }
    

具体的资源文件是否占用磁盘,可以通过下面的方式进行查看


-EOF-

如果感觉此文对你有帮助,请随意打赏支持作者 😘

chengway

认清生活真相之后依然热爱它!

Subscribe to Talk is cheap, Show me the world!

Get the latest posts delivered right to your inbox.

or subscribe via RSS with Feedly!