最近看到的Slow App Startup Times里提到:
The dynamic loader finds and reads the dependent dynamic libraries (dylibs) used by the App. Each library can itself have dependencies. The loading of Apple system frameworks is highly optimized but loading your embedded frameworks can be expensive. To speed up dylib loading Apple suggests you use fewer dylibs or consider merging them A suggested target is for six extra (non-system) frameworks.
至于什么是static lib什么是dynamic lib這里就不展開提了,大家可以通過(guò)在文章底部的相關(guān)鏈接去詳細(xì)了解苛骨。
引用一下官方的解釋就是:
- A better approach is for an app to load code into its address space when it’s actually needed, either at launch time or at runtime. The type of library that provides this flexibility is called dynamic library.
- When an app is launched, the app’s code—which includes the code of the static libraries it was linked with—is loaded into the app’s address space.Applications with large executables suffer from slow launch times and large memory footprints
如果給你一個(gè)簡(jiǎn)單的概念就是如果你的framework使用了swift就是dynamic lib悲幅。這也是本文要對(duì)比的主題:動(dòng)態(tài)的framework對(duì)app啟動(dòng)時(shí)間影響到底多大它呀。
測(cè)試的方法就采用 iOS 10 提供的新的環(huán)境變量 DYLD_PRINT_STATISTICS
輸出的app啟動(dòng)時(shí)間啊片。Xcode的版本是8.1骑脱,測(cè)試設(shè)備是iphone 6约计。cocoapod版本1.1。
注意疫向,測(cè)試過(guò)程發(fā)現(xiàn)每次獲得的時(shí)間統(tǒng)計(jì)都不一致,所以我這里的數(shù)據(jù)可能和你自己測(cè)試得到的不同豪嚎,但是我認(rèn)為這種偏差不影響定性搔驼。
基準(zhǔn)線:空的OC項(xiàng)目 VS 空的Swift項(xiàng)目
創(chuàng)建兩個(gè)沒有任何業(yè)務(wù)邏輯的空的項(xiàng)目。
大概有10毫秒的差異侈询。這個(gè)差距考慮到測(cè)量的偏差可以認(rèn)為兩者幾乎是一致的舌涨。
但是有時(shí)會(huì)出現(xiàn)swift加載忽然提高到400ms的情況。這是因?yàn)橄到y(tǒng)的動(dòng)態(tài)framework只會(huì)加載一份扔字。假設(shè)10個(gè)app啟動(dòng)都用到了UIKit囊嘉,系統(tǒng)內(nèi)部也只加載了一份UIKit。所以有時(shí)swift項(xiàng)目啟動(dòng)的時(shí)候剛好用到了系統(tǒng)framework沒有緩存革为,就會(huì)顯得的長(zhǎng)一點(diǎn)扭粱。
6個(gè)framework
現(xiàn)在我們對(duì)比有代碼的情況。兩個(gè)項(xiàng)目分別加入5個(gè)依賴震檩。
這是OC項(xiàng)目的6個(gè)依賴:
pod 'AFNetworking', '~> 3.0'
pod 'Masonry'
pod 'MJRefresh'
pod 'SDWebImage'
pod 'MBProgressHUD'
pod 'IQKeyboardManager'
這是swift項(xiàng)目的6個(gè)依賴琢蛤,為了模擬真實(shí)生產(chǎn)中依然使用一些OC庫(kù)的情況,將3個(gè)庫(kù)換成了swift編碼的,保留了3個(gè)OC庫(kù):
pod 'Alamofire'
pod 'SnapKit'
pod 'MJRefresh'
pod 'Kingfisher'
pod 'MBProgressHUD'
pod 'IQKeyboardManager'
OC的啟動(dòng)時(shí)間在70-100ms左右虐块。這里取快的情況的數(shù)據(jù):
swift項(xiàng)目在第一次安裝時(shí)的啟動(dòng)時(shí)間在dylib會(huì)多100ms俩滥,不知為何。swift項(xiàng)目在已經(jīng)安裝后運(yùn)行打開的成績(jī):
列一個(gè)橫向?qū)Ρ葓D:
結(jié)論
swift項(xiàng)目的dylib時(shí)間相比OC多了一百多毫秒贺奠,遠(yuǎn)遠(yuǎn)大于OC霜旧。
Swift 不使用framework VS 使用framework
我們猜測(cè)是不是加載framework導(dǎo)致時(shí)間增大很多呢。所以我們把這些依賴的代碼全部放在主app里儡率,不使用framework分離挂据。這次我們把幾個(gè)依賴的庫(kù)全部換成swift:
pod 'Alamofire'
pod 'SnapKit'
pod 'Kingfisher'
pod 'SwiftDate'
pod 'ObjectMapper'
5個(gè)依賴的項(xiàng)目在app已經(jīng)安裝,運(yùn)行打開結(jié)果:
考慮到誤差可以認(rèn)為framework里的代碼是OC還是swift對(duì)于加載時(shí)間的影響并不大儿普。
把依賴的代碼全都合并到App里崎逃,就是采取手工拷貝的方式:
安裝后打開的啟動(dòng)時(shí)間統(tǒng)計(jì):
對(duì)比圖:
結(jié)論
如果把代碼收到拷貝進(jìn)app里,可以顯著降低dylib加載時(shí)間眉孩。
15個(gè)framework OC VS Swift
OC的podfile:
pod 'AFNetworking', '~> 3.0'
pod 'Masonry'
pod 'MJRefresh'
pod 'SDWebImage'
pod 'MBProgressHUD'
pod 'IQKeyboardManager'
pod 'FMDB'
pod 'GPUImage'
pod 'OpenUDID'
pod 'DateTools'
pod 'TMCache'
pod 'WebViewJavascriptBridge'
pod 'ZBarSDK'
pod 'JSQMessagesViewController'
pod 'YYKit'
pod 'DZNEmptyDataSet'
swift項(xiàng)目的podfile:
pod 'AFNetworking', '~> 3.0'
pod 'Alamofire'
pod 'SnapKit'
pod 'MJRefresh'
pod 'Kingfisher'
pod 'MBProgressHUD'
pod 'IQKeyboardManager'
pod 'FMDB'
pod 'GPUImage'
pod 'OpenUDID'
pod 'SwiftDate'
pod 'CryptoSwift'
pod 'ObjectMapper'
pod 'ZBarSDK'
pod 'CocoaLumberjack/Swift'
pod 'YYKit'
pod 'DZNEmptyDataSet'
OC的結(jié)果:
swift的結(jié)果:
對(duì)比圖:
縱向?qū)Ρ龋?/p>
結(jié)論
OC的項(xiàng)目隨著依賴增多个绍,初始化時(shí)間增大,但是dylib時(shí)間增加不明顯浪汪。swift則dylib時(shí)間大幅增加巴柿,初始化時(shí)間變化不大∷涝猓總的啟動(dòng)時(shí)間比OC多了200多毫秒广恢,隨著framework的增多,啟動(dòng)時(shí)間差距拉大呀潭。
25個(gè)dynamic framework
如果把dylib提高到25個(gè)時(shí)間又會(huì)增長(zhǎng)多少呢钉迷。
在15個(gè)基礎(chǔ)上再添加10個(gè)依賴:
pod 'EZSwiftExtensions'
pod 'ReactiveCocoa', '5.0.0-alpha.3'
pod 'RxSwift', '~> 3.0'
pod 'RxCocoa', '~> 3.0'
pod 'MonkeyKing'
pod 'Reusable'
pod 'SwiftyJSON'
pod 'XCGLogger'
pod 'Gifu', '~> 2.0.0-rc'
pod 'Spring', :git => 'https://github.com/MengTo/Spring.git', :branch => 'swift3'
運(yùn)行時(shí)間:
和15個(gè)dylib的時(shí)候的對(duì)比:
dylib加載時(shí)間從400增加到600,增加了30%左右钠署。dylib數(shù)量從15到25個(gè)增加40%糠聪。接近線性。
總結(jié)
毫無(wú)疑問谐鼎,使用了dynamic framework后會(huì)增加app啟動(dòng)時(shí)間枷颊。如果你的數(shù)量在25個(gè)左右,相比OC的靜態(tài)framework啟動(dòng)時(shí)間會(huì)增加0.5s左右该面。我個(gè)人對(duì)于iOS提高加載framework的時(shí)間不太抱有希望,蘋果讓自定義的framework常駐內(nèi)存似乎也無(wú)望信卡,這個(gè)時(shí)間短期內(nèi)可能無(wú)法抹平隔缀。
如果人為的把一些外部依賴手動(dòng)管理在一個(gè)framework也是可行,但是如果復(fù)雜一點(diǎn)包的互相依賴的情況會(huì)比較費(fèi)心傍菇。
如果公司對(duì)性能有著苛刻要求可能500ms是難以忍受的猾瘸。但是我覺得對(duì)于大多數(shù)產(chǎn)品而言,犧牲這500ms的性能相比于使用OC,我覺得還是用OC比較難受牵触。
歡迎關(guān)注我的微博:@沒故事的卓同學(xué)
相關(guān)鏈接:
WWDC 2016 Session 406 Optimizing App Startup Time
What are Frameworks?
iOS 開發(fā)中的『庫(kù)』(一)
jverkoey/iOS-Framework