iOS 啟動優(yōu)化(上)

相關(guān)概念

App 啟動分類

  • 冷啟動

內(nèi)存中不包含App的相關(guān)數(shù)據(jù)本刽,必須從磁盤加載到內(nèi)存(即 App 被 kill 掉,重新打開啟動過程)是由系統(tǒng)所決定的

  • 熱啟動

就是 App 已經(jīng)打開過屋群,但是按下 home 鍵進入后臺,這個時候 App 還存在一段時間鬼店。點擊 App踢涌,立馬就恢復(fù)到原狀態(tài)的過程即為熱啟動

啟動耗時統(tǒng)計

我們一般統(tǒng)計 App 的啟動耗時是從點擊 App 開始到首屏渲染完成的過程所消耗的時間。那么我們?nèi)ソy(tǒng)計耗時呢攘残?啟動檢測一般會以 main 函數(shù)作為一個分界點:main 函數(shù)之前稱為 pre_main 階段拙友,是由 DYLD 反饋應(yīng)用耗時;main 函數(shù)之后由開發(fā)者自己檢測(main 函數(shù)開始計時歼郭,到首屏渲染完成結(jié)束)

冷啟動優(yōu)化

pre_main(main 函數(shù)之前)

打開需要檢測耗時的項目遗契,Xcode -> Product -> Scheme -> Edit Scheme

彈出界面后,依次點擊 Run -> Arguments -> Environment Variables 點擊 + 添加一個變量名 DYLD_PRINT_STATISTICS病曾,并將其值設(shè)置為 1

此時牍蜂,在項目的 main 函數(shù)處打個斷點,運行項目知态,就可以在控制臺上看到 pre_main 耗時時間捷兰,如下:

字段含義
  • dylib loading time

動態(tài)庫加載所需要時間,這個過程會去加載 app 使用的動態(tài)庫负敏,而動態(tài)庫之間有它自己的依賴關(guān)系,所以會消耗時間去查找和讀取秘蛇。

  • rebase/binding time

修正符號和綁定符號耗時其做。

Rebase:在鏡像(MachO文件)內(nèi)部調(diào)整指針的指向,針對mach-o在加載到內(nèi)存中不是固定的首地址(ASLR)這一現(xiàn)象做數(shù)據(jù)修正的過程赁还。
iOS4.3后引入了 ASLR 妖泄,MachO會被加載到隨機地址,這個隨機的地址跟代碼和數(shù)據(jù)指向的舊地址會有偏差艘策。dyld 需要修正這個偏差蹈胡,做法就是將 dylib 內(nèi)部的指針地址都加上這個偏移量。

binding:將指針指向鏡像(MachO文件)外部的內(nèi)容朋蔫,binding就是將這個二進制調(diào)用的外部符號進行綁定的過程罚渐。

  • ObjC setup time

1、讀取二進制文件的 DATA 段內(nèi)容驯妄,找到與 objc 相關(guān)的信息
2荷并、注冊 Objc 類,ObjC Runtime 需要維護一張映射類名與類的全局表青扔。當加載一個 MachO 時源织,它定義的所有的類都需要被注冊到這個全局表中翩伪;
3、讀取 protocol 以及 category 的信息谈息,把category的定義插入方法列表 (category registration)

  • initializer time:

1缘屹、Objc的+load()函數(shù)
2、C++的構(gòu)造函數(shù)屬性函數(shù) 形如attribute((constructor))

優(yōu)化方案

動態(tài)庫加載優(yōu)化

  1. 系統(tǒng)動態(tài)庫已做了高度優(yōu)化侠仇,所以從效率的角度來說轻姿,盡可能使用系統(tǒng)庫。
  2. 自定義的動態(tài)庫比較耗時傅瞻,官方建議盡量少的使用自定義的動態(tài)庫踢代,使用不要超過 6 個動態(tài)庫,多于 6 個的話建議合并嗅骄。
  3. 在性能上出發(fā)將動態(tài)庫編譯成靜態(tài)庫也會優(yōu)化這部分時間胳挎。

ObjC setup 優(yōu)化

  1. 不刻意的去減少幾個類,但是可以避免浪費溺森。
  2. 很多模塊和方法已經(jīng)被廢棄但是卻一直留存在項目中慕爬,可以刪除或保存在其他分支。
  3. 使用一些工具來查找項目中沒有被用到的文件屏积,從而達到優(yōu)化医窿。

initializer

  1. 將不必須在 +load 方法中做的事情延遲到 +initialize 中。因為 +load 方法是在 app 啟動的時候就被調(diào)用炊林,而 +initialize 方法則是在類(Class)第一次使用的時候才調(diào)用姥卢,相當于是懶加載了≡郏可以把 +load 中的代碼移到 +initialize中独榴,并結(jié)合 dispatch_once 來防止重復(fù)調(diào)用。
  2. 如果可以奕枝,盡量減少 C++ 函數(shù)調(diào)用

main 函數(shù)之后

針對 main 函數(shù)之后的時間檢測就通過打點記錄棺榔。在 main()didFinishLaunchingWithOptions 以及第一個頁面的 viewDidAppear 中打點隘道,進行記錄症歇,從而來計算 main 函數(shù)之后的時間。

main 函數(shù)階段耗時

這里我們需要借助 Xcode 提供的 Instruments 工具了谭梗,打開項目忘晤,Xcode -> Open Developer Tool -> Instruments

  1. 進入 Instruments,選擇 Time Profiler 檢測工具默辨,雙擊打開
  1. 選擇需要測試的真機以及測試的 app德频,點擊下面的 Call Tree,勾選 Hide System Libraries(隱藏系統(tǒng)庫)和 Separate by Thread(按線程分類)缩幸,這樣設(shè)置運行的時候就可以看到某個頁面某個函數(shù)運行的時間了
  1. 點擊左上角的運行按鈕壹置,就可以看到耗時操作了竞思。點擊展開可以查詢具體耗時時間,發(fā)現(xiàn)是16進制地址
  1. 針對上面16進制地址钞护,我們需要在 Xcode 中進行設(shè)置下盖喷,才能發(fā)現(xiàn)具體的函數(shù)運行耗時時間
  • 查看裝在真機上的 app 是哪種模式

打開 Xcode -> Product -> Scheme -> Edit Scheme -> Run -> Info,當前的 Build Configuration

  • 設(shè)置該模式下對應(yīng)的運行信息格式為 DWARF with dSYM File
  1. 設(shè)置完成后难咕,重新 Run 一次項目到手機上课梳,然后再進行1-4步驟操作,就可以發(fā)現(xiàn)發(fā)現(xiàn)具體的函數(shù)運行耗時時間了
  1. 在手機上點擊不同的頁面就可以查詢到每個頁面耗時時間余佃,可以根據(jù)自己的項目對耗時比較長的函數(shù)進行優(yōu)化了

main 函數(shù)之后耗時優(yōu)化可操作空間比較大暮刃,需要根據(jù)自己項目情況進行處理。 didFinishLaunchingWithOptions 可以將一些耗時又非必須立馬使用到功能代碼延后加載

熱啟動優(yōu)化

  1. 數(shù)據(jù)優(yōu)化,將耗時操作做異步處理.
  2. 檢查 NSUserDefaults 的存儲爆土,NSUserDefaults 實際上是在 Library 文件夾下會生產(chǎn)一個 plist 文件椭懊,加載的時候是整個 plist 配置文件全部加載到內(nèi)存中。所以非常頻繁的存取大量數(shù)據(jù)也是有可能導(dǎo)致APP啟動卡頓的步势。

拓展-調(diào)試第三方應(yīng)用

首先氧猬,我們需要創(chuàng)建一個空工程,在真機上運行(利用這個工程的描述文件)
其次坏瘩,我們利用腳本文件來輔助盅抚,代碼如下


# ${SRCROOT} 是工程文件所在的目錄
TEMP_PATH="${SRCROOT}/Temp"
#資源文件夾,我們提前在工程目錄下新建一個 wechat_objc 文件夾倔矾,里面放需要調(diào)試的 ipa 包
ASSETS_PATH="${SRCROOT}/wechat_objc"
#目標 ipa 包路徑
TARGET_IPA_PATH="${ASSETS_PATH}/*.ipa"
#清空Temp文件夾
rm -rf "${SRCROOT}/Temp"
mkdir -p "${SRCROOT}/Temp"


#----------------------------------------
# 1. 解壓 IPA 到 Temp 下
unzip -oqq "$TARGET_IPA_PATH" -d "$TEMP_PATH"
# 拿到解壓的臨時的 APP 的路徑
TEMP_APP_PATH=$(set -- "$TEMP_PATH/Payload/"*.app;echo "$1")
# echo "路徑是:$TEMP_APP_PATH"


#----------------------------------------
# 2. 將解壓出來的 .app 拷貝進入工程下
# BUILT_PRODUCTS_DIR 工程生成的 APP 包的路徑
# TARGET_NAME target名稱
TARGET_APP_PATH="$BUILT_PRODUCTS_DIR/$TARGET_NAME.app"
echo "app路徑:$TARGET_APP_PATH"

rm -rf "$TARGET_APP_PATH"
mkdir -p "$TARGET_APP_PATH"
cp -rf "$TEMP_APP_PATH/" "$TARGET_APP_PATH"


#----------------------------------------
# 3. 刪除 extension 和 WatchAPP. 個人證書沒法簽名 Extention
rm -rf "$TARGET_APP_PATH/PlugIns"
rm -rf "$TARGET_APP_PATH/Watch"


#----------------------------------------
# 4. 更新 info.plist 文件 CFBundleIdentifier
#  設(shè)置:"Set : KEY Value" "目標文件路徑"
/usr/libexec/PlistBuddy -c "Set :CFBundleIdentifier $PRODUCT_BUNDLE_IDENTIFIER" "$TARGET_APP_PATH/Info.plist"


#----------------------------------------
# 5. 給 MachO 文件上執(zhí)行權(quán)限
# 拿到 MachO 文件的路徑
APP_BINARY=`plutil -convert xml1 -o - $TARGET_APP_PATH/Info.plist|grep -A1 Exec|tail -n1|cut -f2 -d\>|cut -f1 -d\<`
#上可執(zhí)行權(quán)限
chmod +x "$TARGET_APP_PATH/$APP_BINARY"


#----------------------------------------
# 6. 重簽名第三方 FrameWorks
TARGET_APP_FRAMEWORKS_PATH="$TARGET_APP_PATH/Frameworks"
if [ -d "$TARGET_APP_FRAMEWORKS_PATH" ];
then
for FRAMEWORK in "$TARGET_APP_FRAMEWORKS_PATH/"*
do

#簽名
/usr/bin/codesign --force --sign "$EXPANDED_CODE_SIGN_IDENTITY" "$FRAMEWORK"
done
fi


#注入
#yololib "$TARGET_APP_PATH/$APP_BINARY" "Frameworks/HankHook.framework/HankHook"

以上代碼可以寫成一個 .sh 的腳本文件或者直接將代碼粘貼到調(diào)試工程中(每一步都有相應(yīng)的解釋妄均,根據(jù)個人需求更改)

示例

新建一個空工程 ObjC_Test,在工程根目錄下新建文件夾 wechat_objc哪自,并將脫殼的 iPA 包放進去

修改上述代碼存放第三方 ipa 的路徑

將上述代碼生成 appSign.sh 腳本文件并放到工程目錄下

給工程添加腳本

配置腳本(./ 表示工程根目錄丛晦,即加載根目錄的 appSign.sh 腳本)

運行應(yīng)用,此時就可以將三方的應(yīng)用運行到真機了提陶。下面是打印第三方(wechat)的啟動時間

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市匹层,隨后出現(xiàn)的幾起案子隙笆,更是在濱河造成了極大的恐慌,老刑警劉巖升筏,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件撑柔,死亡現(xiàn)場離奇詭異,居然都是意外死亡您访,警方通過查閱死者的電腦和手機铅忿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灵汪,“玉大人檀训,你說我怎么就攤上這事柑潦。” “怎么了峻凫?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵渗鬼,是天一觀的道長。 經(jīng)常有香客問我荧琼,道長譬胎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任命锄,我火速辦了婚禮堰乔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘脐恩。我一直安慰自己镐侯,他們只是感情好,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布被盈。 她就那樣靜靜地躺著析孽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪只怎。 梳的紋絲不亂的頭發(fā)上袜瞬,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天,我揣著相機與錄音身堡,去河邊找鬼邓尤。 笑死,一個胖子當著我的面吹牛贴谎,可吹牛的內(nèi)容都是我干的汞扎。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼擅这,長吁一口氣:“原來是場噩夢啊……” “哼澈魄!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起仲翎,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤痹扇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后溯香,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鲫构,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年玫坛,在試婚紗的時候發(fā)現(xiàn)自己被綠了结笨。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖炕吸,靈堂內(nèi)的尸體忽然破棺而出伐憾,到底是詐尸還是另有隱情,我是刑警寧澤算途,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布塞耕,位于F島的核電站,受9級特大地震影響嘴瓤,放射性物質(zhì)發(fā)生泄漏扫外。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一廓脆、第九天 我趴在偏房一處隱蔽的房頂上張望筛谚。 院中可真熱鬧,春花似錦停忿、人聲如沸驾讲。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吮铭。三九已至,卻和暖如春颅停,著一層夾襖步出監(jiān)牢的瞬間谓晌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工癞揉, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留纸肉,地道東北人。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓喊熟,卻偏偏與公主長得像柏肪,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子芥牌,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容