你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發綜合 >> iOS9-by-Tutorials-學習筆記四:APP-瘦身

iOS9-by-Tutorials-學習筆記四:APP-瘦身

編輯:IOS開發綜合

iOS9-by-Tutorials-學習筆記四:APP-瘦身

這篇文章在書中的標題是App Thinning,這裡我給翻譯成了App 瘦身。

本文然然進行了一些語法的修改,很開心她為我修改這些東西。她說我轉折只會用但是,被她這麼一說想想還真是只是會用但是,嘿嘿。

iPhone經過這幾年的發展,已經發生了很大的變化,例如屏幕變得更加多樣,尺寸更多,內存變得更大,CPU的架構也在變化。伴隨著iPhone的變化,iOS也在變化,例如AutoLayout、size classes、split view controller等。這些技術及設備的變化給我在開發的過程中也造成了許多的問題,不僅如此蘋果通過不斷推出新的技術,努力在幫助我們使用同一套代碼開發適應多個設備的Universal的App。另一方面Universal App雖然在開發的過程中,方便了我們開發人員,可是對於用戶來說就不那麼友好了,由於需要適配多種設備,所以裡包含所有設備的代碼,但真正的在運行的時候,我們並不需要那麼多相關的代碼及資源。

例如下面的一張圖,是一個App運行在iPhone 6+上,使用的各個資源相關的情況:
\

上圖中對勾標出來的是在iPhone 6+上真實運行的時候使用到的相關的資源及代碼,對比有對勾的部分,更多的是沒有被對勾標出來的部分。可以想象我們下載了一個App(前提這個App是Universal的),然後至少一半的代碼及資源是我們不需要的,白白占用著我們的空間。這樣對用戶體驗也不好。為了解決這個問題蘋果在iOS 9給出了新的解決方案:

App Slicing 當你提交你的iOS 9 打包文件到App Store的時候,蘋果編譯你的資源和可執行文件,然後為每個設備生成一個特定的可執行文件。最終,設備只會下載適應與其特性的,並且它使用到的內容。這些特性包含顯卡性能(原文單詞:graphics capabilities)、內存級別、CPU架構、size classes、屏幕 scaling等。 On Demand Resouces 應用程序的資源只有在需要使用的時候才會下載,並且如果其他資源需要空間這些資源可以被移除。 Bitcode 在你提交App到App StZ喎?/kf/ware/vc/" target="_blank" class="keylink">vcmW1xMqxuvKjrEJpdGNvZGW/ydLU1/fOqtbQvOSy+s7v0rvG8Mzhvbuho7D8uqxiaXRjb2RlxeTWw7XEs8zQ8r2ru+HU2kFwcCBzdG9yZcnPsbux4NLrus3BtL3ToaNiaXRjb2Rl1MrQ7ca7ufvU2rrzxtrW2NDC08W7r87Sw8ezzNDytcS2/r341sbOxLz+o6y2+LK70OjSqs7Sw8fW2NDCzOG9u9K7uPbQwrXEsOaxvrW9QXBwIHN0b3Jlyc+how0KPHA+1eLI/bj2vLzK9bzTxvDAtKOszbPSu7PGzqpBcHAgVGhpbm5pbmehozwvcD4NCjxoMyBpZD0="getting-started">Getting started

打開本章節的初始項目,然後選在iPad Air 2運行,這時候運行效果如下:
\

伴隨著模擬器啟動起來的還打開了一個Finder窗口:

這個Finder窗口能夠打開,是因為在程序中添加了一個腳本,每次運行的時候都會執行,腳本所在地方如下:

echo "App Size in KB:  `du -sk \"${CONFIGURATION_BUILD_DIR}/${EXECUTABLE_NAME}.app\"`"
if [ "${CONFIGURATION}" = "Debug" ]; then
open ${CONFIGURATION_BUILD_DIR}
fi

在Finder的Old CA Maps點擊右鍵,選擇顯示包內容,如下:
\

上圖中標注的說明如下:
1. Assets.car是Assets.xcassets被Xcode進行編譯後的文件。
2. Old CA Maps是真實運行在設備上的可執行文件。
3. Santa Cruz PNGs 這個是圖片文件,但是沒有被編譯到Assets.car文件中,這是因為它並沒有放到Assets.xcassets中,而是放到了工程的頂層文件中。
4. SD_Map.bundle 這個就是地圖圖片文件,但是將近120MB。

Measuring your work

本章介紹一些App瘦身相關的東西,所以我們必須能夠測量App是否減少了。工程裡面已經內置了一個腳本(上面代碼裡面有),能夠在build的過程中輸出App的大小。查看的位置如下:
\

Slicing up app slicing

App slicing包含兩部分內容:可執行文件分片(Executable slicing)和資源分片(resource slicing)。

Executable slicing 指的是在設備下載App的時候會根據設備的相關信息只是下載對應該設備的相關的可執行文件,並不會包含其他設備及架構的可執行文件,達到App安裝包的縮小。並且這個功能並不需要我們做太多,App Store默認支持的。

默認情況下提交到App Store的包是包含所有的內容的,這些都在配置文件裡面,App Store會自動創建對應於每個類型的可執行文件。這個在iOS9+上支持。

Being smart with resources

Resource slicing 需要我們一小部分簡單的工作就能實現。如果使用Resource slicing,則要保證我們的資源都被Asset Catalogs管理。在Xcode 7中,能夠標記資源被使用設備的 MemoryGraphics ,如下:
\

Your first fix

在開始的時候介紹過Santa Cruz PNGs這個文件因為被放到Main bundle中,所以不能被編譯進入到Assets.car,進而也不能使用Resource slicing。下面看一下我們怎麼修改,使其能夠使用:
\

選擇New Image Set後,將新加入的set命名為Santa Cruz,緊接著做如下操作:

糾正一下 上圖左邊的內容應該是刪除,包括在Finder內也應該刪除

然後在不同的設備上運行App,最後發現Asset.car文件的大小並不一致。這個是因為在安裝的時候,會根據設備安裝對應的資源。

Lazily (down)loading content

蘋果提供On-Demand Resources技術,簡稱ODR。ODR允許你將資源存儲在蘋果的服務器上,然後在你App使用的時候再去下載。NSBundleResourceRequest是處理ODR的類,使用這個類能夠通過tag下載對應的資源。images, data, OpenGL shaders, SpriteKit Particles, Watchkit Complications等都可以使用ODR。

Wire things up to use tags

下面我們修改代碼,實現資源的下載,修改MapChromeViewController.swift對應方法如下:

  private func downloadAndDisplayMapOverlay() {
//    displayOverlayFromBundle(NSBundle.mainBundle())
    guard let bundleTitle = mapOverlayData?.bundleTitle else {
      return
    }

    let bundleResource = NSBundleResourceRequest(tags: [bundleTitle])

    bundleResource.beginAccessingResourcesWithCompletionHandler { [weak self] (error) -> Void in
      NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
        if error == nil {
          self?.displayOverlayFromBundle(bundleResource.bundle)
        }
      })
    }

  }

這時候我們運行代碼,可能會在控制台輸出錯誤,這是因為我們對應的bundle並沒有tag,我們需要給bundle添加tag:
\

然後我們重新編譯運行我們的程序,然後按照上面的查看編譯運行的程序的大小,發現小了許多。對比之前的編譯生成的文件,發現運行文件裡面不包含bundle了。

如果你的App在App Store上可能這個資源文件下載的很慢。但是在開發的過程中,Xcode會利用本地網絡作為服務器,然後在設備上能夠下載到,所以在開發的過程中如果電腦關了,那ODR也就不能使用了。

Make it download faster

在我們使用ODR的過程中,如果bundle比較大,可能再下載的過程中就會比較耗時,並且在下載過程中用戶不知道,這樣用戶體驗就不好。我們可以再Resource下載的過程中給用戶一些提示,修改下面的代碼:

// add 為新添加的 ProgressView是程序已經添加上的
private func downloadAndDisplayMapOverlay() {
//    displayOverlayFromBundle(NSBundle.mainBundle())
  guard let bundleTitle = mapOverlayData?.bundleTitle else {
    return
  }

  let bundleResource = NSBundleResourceRequest(tags: [bundleTitle])

  bundleResource.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent  //add

  loadingProgressView.observedProgress = bundleResource.progress // add

  loadingProgressView.hidden = false // add
  UIApplication.sharedApplication().networkActivityIndicatorVisible = true // add

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

}

如果用戶已經下載過某個bundle,下次在使用的時候就不會再去下載了。

The many flavors of tagging

雖然添加了ProgressView,在體驗是好了一點,但是需要注意測試的時候是使用的本地的網絡,所以比較快,但是如果要是提交到App Store上,那可能下載就是比較慢了,如果再配上用戶沒有WiFi那可能就沒法用了,所以我們還需要做其他的一些調整。

Initial install tags

使用Initial install tags,我們可以設置哪些bundle會在我們App初始化安裝的時候就會被下載。 下面下介紹一下ODR三種下載的時機吧:
* Initial Install Tags 在ipa下載的時候一同下載
* Prefetched Tag Order 在程序下載完成後,下載對應的資源,然後按順序排列。
* Prefetched Tag Order 按需下載
下面是配置的地方:
\

Purging content

應用程序在使用的過程中通過ODR下載了對應的bundle,但是有時候我們需要清理一些已經下載過的並且不使用的bundle。在介紹怎麼刪除之前先看一下怎麼查看下載的ODR:
\

Set a resource to be purged

在MapChromeViewController.swift添加如下代碼:

  // new add 是新加的代碼
  var overlayBundleResource: NSBundleResourceRequest? // new add
  private func downloadAndDisplayMapOverlay() {
//    displayOverlayFromBundle(NSBundle.mainBundle())
    guard let bundleTitle = mapOverlayData?.bundleTitle else {
      return
    }

    let bundleResource = NSBundleResourceRequest(tags: [bundleTitle])
    overlayBundleResource = bundleResource // new add

    bundleResource.loadingPriority = NSBundleResourceRequestLoadingPriorityUrgent  //add

    loadingProgressView.observedProgress = bundleResource.progress // add

    loadingProgressView.hidden = false // add
    UIApplication.sharedApplication().networkActivityIndicatorVisible = true // add

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

  }

  // new add
  override func viewDidDisappear(animated: Bool) {
    super.viewDidDisappear(animated)
    // 告訴系統結束了對資源的訪問
    overlayBundleResource?.endAccessingResources()
  }

上面的代碼,我做測試的時候不清楚會在什麼時候會刪除,我也模擬了內存警告,如果誰清楚,還請告訴我,謝謝。

堅持了好幾天中午寫完了,這篇筆記,一篇筆記13張截圖,好累。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved