在筆者3月份為Apple Watch開發RebelSheep小游戲進行真機測試的時候,就發現了目前Apple Watch上第三方應用的性能并不理想,而測試部分Watch App Store里推薦的應用甚至都沒能成功開啟。盡管Watch OS 1.01已經提升了應用啟動的速度,但用戶普遍感受還是體驗較差,因此我們有必要盡全力優化自己的Apple Watch應用。筆者結合自己的體會和其他先驅者的一些心得,對相關技巧做了一些匯總,分設計優化和資源優化兩方面來說一下。
優化目標:縮短WatchApp的啟動時間,提升響應速度
代碼環境:Swift 1.2、Xcode6.3.2
與iPhone應用可以充分發揮藝術品質的設計不同,Apple Watch應用必須遵循簡單、直接、輕量級的原則,因此在整個軟件界面及程序架構模型設計上就必須全面考慮。Apple官方文檔《WatchKit Development Tips》里提到,為了提升性能,Apple Watch應用應實現:少的通信量、只更新變化的內容、延遲加載內容、快速初始化分頁控制器、簡化控制器場景、減少表格初始顯示行數。下面我們具體看一下:
WatchKit擴展應用開發目前面臨的一個很大麻煩就是UI組件的狀態都是可寫而不可讀的,這樣每次刷新界面內容時很難判斷哪些是變動的數據而不得不把屏幕上所有內容都更新一遍。RobinSenior在這篇文章里提出利用視圖模型的存儲可以減少通信量和實現僅更新變化對象的數據。其實道理很簡單,就是實現一個協議,判斷原始內容是否和新內容一致。
比如對于標簽控件WKInterfaceLabel,利用以下代碼可以實現僅當標簽文本發生變化時才更新標簽內容。
[cpp] view plaincopy![]()
protocol Updatable {
typealias T
func updateFrom(oldValue : T?, to newValue : T?)
}
extension WKInterfaceLabel : Updatable {
func updateFrom(oldValue:String?, to newValue:String?){
if newValue != oldValue {
self.setText(newValue)
}
}
}
對于WKInterfaceImage,可利用此思路實現圖像下載的網絡地址改變時才去下載緩存圖像并更新圖片,對于WKInterfaceTable可以實現讀取表格后續數據行直接在表格后附加而不用刷新整個表格等等,這里不多贅述。
為了優化Watch App的啟動速度和響應能力,我們的程序設計上需要考慮初始化時只加載本屏顯示的內容,滾屏顯示的額外內容延遲加載。而使用dispatch_async異步方式去處理耗時長的界面圖像元素加載等任務將能夠更快的提前呈現視圖控制器。大致代碼結構如下:
[cpp] view plaincopy![]()
override func willActivate() {
super.willActivate()
dispatch_async(dispatch_get_main_queue(), {
//加載界面的圖像元素等長時間操作
})
}
順便一提,利用Swift語言的lazy關鍵字修飾變量,其懶加載機制也可降低初始化工作的壓力。
在使用多頁視圖模式時一定要特別注意,各頁的控制器的init和awakeWithContext會比頁控制器的willActivate更早執行,因此每頁的數據加載等長時間任務有必要放到willActivate函數里運行。另外,每次切換分頁都會執行對應控制器的willActivate函數,而在Watch OS 1.01版里,為了提升性能系統甚至會提前運行下一頁的willActivate,為了少做無用功,我們可以設計一些緩存和避免重復加載的功能。
我們為了實現一些提示功能,可能會在控制器里放置一些隱藏的標簽控件等,但如果數量太多,也會嚴重影響視圖加載速度。而對于表格,前面已經提到,初應該僅加載屏里能看到的行。這些措施都能夠大幅提升響應速度。
1.5.1 狀態保存
Apple官方文檔《WatchKit Development Tips》里建議,我們可以在視圖控制器的willActivate和didDeactivate兩個方法里恢復/保存app的狀態和數據。但很多情況下是不必要的(比如在多視圖場景切換時也會執行有關代碼),一項更好的選擇是利用以下系統通知:
NSExtensionHostWillEnterForegroundNotification
NSExtensionHostDidBecomeActiveNotification
NSExtensionHostWillResignActiveNotification
NSExtensionHostDidEnterBackgroundNotification
我們可以在主視圖控制器的init或awakeWithContext里通過NSNotificationCenter注冊,比如:
[cpp] view plaincopy![]()
override init() {
super.init()
NSNotificationCenter.defaultCenter().addObserver(
self, selector:"onDeactivate", name:"NSExtensionHostWillResignActiveNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(
self, selector:"onBackground", name:"NSExtensionHostDidEnterBackgroundNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(
self, selector:"onForeground", name:"NSExtensionHostWillEnterForegroundNotification", object: nil)
NSNotificationCenter.defaultCenter().addObserver(
self, selector:"onActive", name:"NSExtensionHostDidBecomeActiveNotification",object: nil)
}
上述代碼中NSExtensionHostWillResignActiveNotification對應的應用掛起事件處理方法onDeactivate里就是一個保存應用狀態數據的不錯選擇,相對應的WillEnterForegroundNotification的處理方法里可以讀取回復應用狀態數據。
1.5.2 視圖更新中的風險
雖然按照Apple要求,我們可以只更新界面變化的內容,但值得注意的一點是,如果嘗試更新時視圖卻處于不可見的狀態,那么更新操作將會失被系統忽略而失敗,你也無法得到操作失敗的通知。典型的情況比如:視圖控制器A有一個文本標簽,其內容是時刻變化的,然而控制器A切換/彈出到了視圖控制器B,那么此時更新控制器A的文本標簽內容可能將會失?。ˋ已經處于Deactivated狀態),關閉控制器B返回控制器A時其內容就并非新的。有必要的話請通過設立好標識變量等方式輔助解決此問題。
本站文章版權歸原作者及原出處所有 。內容為作者個人觀點, 并不代表本站贊同其觀點和對其真實性負責,本站只提供參考并不構成任何投資及應用建議。本站是一個個人學習交流的平臺,網站上部分文章為轉載,并不用于任何商業目的,我們已經盡可能的對作者和來源進行了通告,但是能力有限或疏忽,造成漏登,請及時聯系我們,我們將根據著作權人的要求,立即更正或者刪除有關內容。本站擁有對此聲明的最終解釋權。