作為一名程序員推沸,日常開(kāi)發(fā)過(guò)程中少不了各種代碼調(diào)試输枯,這也是程序員的必備技能之一议泵,今天便來(lái)簡(jiǎn)單的聊一聊iOS開(kāi)發(fā)過(guò)程中的一些調(diào)試技巧。
工具準(zhǔn)備:Mac桃熄,iPhone先口,Xcode,Charles,Reveal
1. 斷點(diǎn)調(diào)試
1.1 常規(guī)斷點(diǎn)
斷點(diǎn)調(diào)試是最簡(jiǎn)單的調(diào)試方式之一碉京,在要調(diào)試代碼行的左側(cè)單擊或者代碼光標(biāo)處使用快捷鍵(command+\
)添加斷點(diǎn)厢汹,運(yùn)行項(xiàng)目滿足斷點(diǎn)條件后會(huì)在斷點(diǎn)所在的代碼行處停住,此時(shí)可以配合控制臺(tái)LLDB調(diào)試進(jìn)行代碼調(diào)試谐宙。運(yùn)行圖示如下:
單擊斷點(diǎn)標(biāo)識(shí)可以停用斷點(diǎn)烫葬,右擊斷點(diǎn)標(biāo)識(shí)可以對(duì)斷點(diǎn)進(jìn)行更多操作,比如刪除凡蜻,編輯等等搭综。
1.2 異常斷點(diǎn)
代碼運(yùn)行過(guò)程中可能會(huì)出現(xiàn)各種崩潰,崩潰后Xcode代碼會(huì)卡在main.m文件的mian函數(shù)這划栓,對(duì)于崩潰調(diào)試是沒(méi)有任何幫助的兑巾。那么如何捕捉這種崩潰所發(fā)生的代碼位置呢,這就要用到異常斷點(diǎn)了茅姜。
Xcode
中選中導(dǎo)航中的斷點(diǎn)按鈕闪朱,點(diǎn)擊左下角+
按鈕,選擇Exception Breakpoint...
選項(xiàng)钻洒,給當(dāng)前項(xiàng)目增加異常斷點(diǎn)奋姿,如下圖所示:
斷點(diǎn)增加完后就是圖中左邊的All Objective-C Exception
斷點(diǎn),此時(shí)項(xiàng)目運(yùn)行一旦發(fā)生崩潰素标,Xcode
會(huì)將程序暫停在問(wèn)題代碼所在行處称诗,如圖所示:
圖中代碼模擬了一個(gè)簡(jiǎn)單的crash,給不是NSString
對(duì)象的url調(diào)用了一個(gè)NSString
對(duì)象的length
方法头遭,因?yàn)樵黾恿水惓帱c(diǎn)寓免,程序直接暫停在了crash所發(fā)生的的代碼處,使得問(wèn)題定位更加簡(jiǎn)單方便计维。
1.3 符號(hào)斷點(diǎn)
從一個(gè)案例入手看如何使用符號(hào)斷點(diǎn)袜香。Xcode控制臺(tái)輸出日志如下:
[framework] CUICatalog: Invalid asset name supplied: '(null)'
問(wèn)題原因:定位到原因是[UIImage imageNamed:]
傳入的參數(shù)為nil。但是有一個(gè)問(wèn)題鲫惶,怎么定位到時(shí)哪個(gè)文件的哪些[UIImage imageNamed:]
方法傳入的參數(shù)是nil蜈首。這個(gè)時(shí)候使用Symbolic Breakpoint
斷點(diǎn)調(diào)試的方法,可以讓我們?cè)诮鉀Q這類問(wèn)題時(shí)事半功倍欠母。
- 點(diǎn)擊左下角
+
選擇Symbolic Breakpoint...
- 右鍵斷點(diǎn)選擇
Edit Breakpoint...
- 在
Symbolic
選項(xiàng)中輸入要調(diào)試的目標(biāo)方法欢策,比如[UIImage imageNamed:]
- 在
Condition
選項(xiàng)輸入觸發(fā)目標(biāo)方法斷點(diǎn)的條件,比如$arg3 == nil
運(yùn)行程序赏淌,如果當(dāng)前界面有調(diào)用目標(biāo)方法踩寇,并且條件滿足,就會(huì)進(jìn)入這個(gè)方法的斷點(diǎn)模式六水,進(jìn)入Xcode
的Debug Navigator
選項(xiàng)俺孙,查看調(diào)用堆棧辣卒,棧頂?shù)姆椒ň褪浅霈F(xiàn)問(wèn)題的代碼,直接點(diǎn)擊查看即可鼠冕。
2. LLDB調(diào)試
在斷點(diǎn)調(diào)試的常規(guī)斷點(diǎn)中我們說(shuō)到可以配合控制臺(tái)LLDB調(diào)試方式進(jìn)行代碼調(diào)試添寺,這樣可以起到事半功倍的效果。圖示如下:
Xcode加上普通斷點(diǎn)后運(yùn)行項(xiàng)目懈费,觸發(fā)斷點(diǎn)就可以在控制臺(tái)看到類似上面圖片的內(nèi)容计露,左邊是當(dāng)前代碼塊內(nèi)的可用變量,右邊可以使用LLDB調(diào)試器操作變量進(jìn)行代碼調(diào)試憎乙。
下面看下常用的LLDB調(diào)試命令:
2.1 p
票罐、po
打印當(dāng)前變量值一般使用p
與po
命令。p
等同于print
泞边,會(huì)打印出當(dāng)前變量的變量類型该押、地址信息和變量信息。po
是最常用的打印命令阵谚,會(huì)打印出當(dāng)前變量蚕礼。
2.2 c
、n
c
就是Continue
梢什,繼續(xù)執(zhí)行當(dāng)前程序奠蹬。
n
就是StepOver
,一步一步執(zhí)行當(dāng)前代碼嗡午。
2.3 expression
打印表達(dá)式的值囤躁,不僅改變了調(diào)試器中的值,也改變了程序中變量的值荔睹。這對(duì)于調(diào)試來(lái)說(shuō)非常好用狸演,不用重新運(yùn)行App即可看到修改后的效果,可以為我們的開(kāi)發(fā)調(diào)試過(guò)程節(jié)省大量時(shí)間。
(lldb) po url
https://www.google.com.hk/
(lldb) expression url = @"http://www.baidu.com"
(__NSCFString *) $10 = 0x0000000170459a10 @"http://www.baidu.com"
(lldb) po url
http://www.baidu.com
2.4 b
、br
- 給
DDHomeViewController.m
文件第100行加斷點(diǎn)岔留。
b DDHomeViewController.m:100
- 打印當(dāng)前項(xiàng)目所有斷點(diǎn)信息。
br l
- 刪除第n個(gè)斷點(diǎn)消玄。
br delete n
2.5 image
image
是用來(lái)獲取一個(gè)或多個(gè)目標(biāo)模塊信息的命令。如下丢胚,用來(lái)查看當(dāng)前項(xiàng)目中使用到的依賴庫(kù):
(lldb) image list
[ 0] 047D2C9A-BBEC-307A-AC41-357643679B5F 0x00000001000bc000 /Users/admin/Library/Developer/Xcode/DerivedData/Demo-fafnyyrbwiblnaapporhwiinjobd/Build/Products/Debug-iphoneos/Demo.app/Demo
/Users/admin/Library/Developer/Xcode/DerivedData/Demo-fafnyyrbwiblnaapporhwiinjobd/Build/Products/Debug-iphoneos/Demo.app.dSYM/Contents/Resources/DWARF/Demo
[ 1] A3339F99-C2EA-39D8-BEB7-0B8FF2E84061 0x00000001032dc000 /Users/admin/Library/Developer/Xcode/iOS DeviceSupport/10.3.2 (14F89)/Symbols/usr/lib/dyld
[ 2] DA0F6A86-DB85-3140-B2D7-9E3B36F28795 0x000000018e07e000 /Users/admin/Library/Developer/Xcode/iOS DeviceSupport/10.3.2 (14F89)/Symbols/usr/lib/libc++.1.dylib
如果還想更加詳細(xì)的了解LLDB的相關(guān)信息,可以參考XCODE LLDB TUTORIAL受扳、The LLDB Debugger携龟。
3. Charles調(diào)試
Charles是目前網(wǎng)絡(luò)調(diào)試的主要工具之一,功能類似于Fiddler工具勘高。在日常開(kāi)發(fā)過(guò)程中可以用其抓取接口Request的Headers
峡蟋、Query String
和Cookies
等信息坟桅,以及Response的返回JSON Text
數(shù)據(jù)和數(shù)據(jù)結(jié)構(gòu)等信息來(lái)幫助日常開(kāi)發(fā)調(diào)試工作。安裝和配置教程網(wǎng)上很多蕊蝗,這里不做贅述仅乓,下面我們來(lái)看下Charles的常見(jiàn)調(diào)試技巧。
3.1 抓取請(qǐng)求蓬戚,查看請(qǐng)求信息
在安裝和配置好Charles的前提下夸楣,打開(kāi)Charles,將手機(jī)連接代理到電腦子漩,打開(kāi)想要抓包的應(yīng)用豫喧,如果有請(qǐng)求的話,就會(huì)如上圖左側(cè)所示展示出當(dāng)前手機(jī)的所有請(qǐng)求列表幢泼,找到你想要的查看的請(qǐng)求紧显,選中后在右側(cè)即可看到這個(gè)請(qǐng)求的相關(guān)信息。
- 頂部紅框缕棵,
Overview
項(xiàng)可以查看當(dāng)前請(qǐng)求的概覽信息孵班,比如URL
、Status
招驴、Response
等篙程。Request
項(xiàng)可以查看當(dāng)前請(qǐng)求的Request
信息,比如Headers
忽匈、Query String
房午、Cookies
等。Response
項(xiàng)可以查看請(qǐng)求的返回信息丹允,比如請(qǐng)求的返回信息等郭厌。 - 底部紅框,選中
JSON Text
選項(xiàng)即可查看當(dāng)前請(qǐng)求的返回?cái)?shù)據(jù)和數(shù)據(jù)結(jié)構(gòu)雕蔽。 - 左下角的
Filter
輸入框可以幫我們過(guò)濾掉不需要關(guān)注的請(qǐng)求折柠,只需要輸入想要查看請(qǐng)求的關(guān)鍵詞即可。
3.2 Map Local
開(kāi)發(fā)過(guò)程中如果服務(wù)端的接口數(shù)據(jù)還沒(méi)有準(zhǔn)備好批狐,或者需要修改返回?cái)?shù)據(jù)的某個(gè)值以滿足本地調(diào)試條件扇售,則可以使用本地調(diào)試(Map Local)功能來(lái)完成開(kāi)發(fā)調(diào)試工作。
- 首先在本地準(zhǔn)備一個(gè)JSON文件嚣艇,文件內(nèi)容是你自己處理好的JSON數(shù)據(jù)承冰,比如服務(wù)端沒(méi)有寫(xiě)好的數(shù)據(jù)或者你想要修改的數(shù)據(jù)都可以放在這個(gè)JSON文件中。
- 選中你想要修改返回?cái)?shù)據(jù)的請(qǐng)求食零,右擊選擇
Map Local...
點(diǎn)擊Choose
困乒,找到你已準(zhǔn)備好的JSON文件,點(diǎn)擊Select
贰谣,確認(rèn)配置完畢后點(diǎn)擊OK
娜搂。
- 刷新請(qǐng)求迁霎,這個(gè)時(shí)候你所拿到的數(shù)據(jù)就是修改后的本地?cái)?shù)據(jù),這對(duì)于開(kāi)發(fā)調(diào)試來(lái)可以起到事半功倍的效果百宇。
3.3 Map Remote
比如在做前端開(kāi)發(fā)的時(shí)候搭建了一個(gè)本地服務(wù)器考廉,開(kāi)發(fā)任務(wù)寫(xiě)完后想要把本地環(huán)境請(qǐng)求切到外網(wǎng)的測(cè)試環(huán)境請(qǐng)求,這個(gè)時(shí)候就可以使用Charles的Map Remote
功能來(lái)實(shí)現(xiàn)這個(gè)需求携御。
- 選中你想要修改返回?cái)?shù)據(jù)的請(qǐng)求昌粤,右擊選擇
Map Remote...
- 在彈窗中配置好
Map From
(想要修改的請(qǐng)求)和Map To
(目標(biāo)請(qǐng)求)模塊。 - 點(diǎn)擊
OK
刷新請(qǐng)求因痛,這個(gè)時(shí)候請(qǐng)求的就是Map To
所設(shè)置的目標(biāo)請(qǐng)求了婚苹。
3.4 修改網(wǎng)絡(luò)請(qǐng)求
開(kāi)發(fā)過(guò)程中為了達(dá)到某種目的,可能會(huì)有修改網(wǎng)絡(luò)請(qǐng)求的Cookie
鸵膏、Header
等信息的需求膊升,強(qiáng)大的Charles當(dāng)然也少不了這種調(diào)試功能。
- 左側(cè)請(qǐng)求列表中選中要修改參數(shù)的請(qǐng)求谭企,點(diǎn)擊右側(cè)箭頭指向的鋼筆按鈕(Compose a new request based on the selection)廓译。
- 點(diǎn)擊后就可以通過(guò)下方的
Add
和Remove
對(duì)選中的請(qǐng)求做參數(shù)編輯操作了 - 操作完后點(diǎn)擊下方的
Execute
按鈕,那么根據(jù)選中請(qǐng)求構(gòu)建成一個(gè)新請(qǐng)求的操作就會(huì)被執(zhí)行并重新發(fā)起請(qǐng)求了债查。
修改網(wǎng)絡(luò)請(qǐng)求的操作還可喲通過(guò)Breakpoints
功能來(lái)完成非区,在選中的請(qǐng)求上右擊選擇Breakpoints
,然后點(diǎn)擊Charles頂部的刷新按鈕盹廷,這個(gè)時(shí)候請(qǐng)求會(huì)被中斷征绸,在Charles右邊的面板上就可以Add
和Remove
請(qǐng)求參數(shù)了。
3.5 模擬弱網(wǎng)環(huán)境
開(kāi)發(fā)過(guò)程中如果想要在數(shù)據(jù)請(qǐng)求的過(guò)程中做一些操作俄占,但是網(wǎng)絡(luò)請(qǐng)求很快管怠,你還沒(méi)有操作網(wǎng)絡(luò)請(qǐng)求已經(jīng)執(zhí)行完畢了,這個(gè)時(shí)候弱網(wǎng)環(huán)境模擬功能就排上用場(chǎng)了缸榄。
- 在Charles菜單欄選擇
Proxy --> Throttle Settings...
渤弛,彈窗如圖所示:
- 選中
Enable Throttle
選項(xiàng),即可在彈窗下方設(shè)置網(wǎng)絡(luò)請(qǐng)求參數(shù)了甚带,比如帶寬(下載和上傳)她肯、利用率、可靠性等等鹰贵。 - 配置完后點(diǎn)擊
OK
即可使用配置好的弱網(wǎng)環(huán)境開(kāi)發(fā)調(diào)試了晴氨。
3.6 解析pb格式數(shù)據(jù)
如果項(xiàng)目中接口使用了ProtoBuf數(shù)據(jù),在使用Charles抓包時(shí)看到的請(qǐng)求和返回?cái)?shù)據(jù)將是一堆亂碼碉输,可讀性極差籽前,對(duì)于想獲取這個(gè)接口信息的人來(lái)說(shuō)是非常不友好的。想要看到真實(shí)的請(qǐng)求和返回?cái)?shù)據(jù)呢,通過(guò)在Charles中給該接口配置desc
文件即可聚假,下面看具體實(shí)現(xiàn)。
- 生成
desc
文件
進(jìn)入到proto文件所在目錄闰非,執(zhí)行下面命令膘格,當(dāng)然命令執(zhí)行成功的前提已經(jīng)安裝了protoc工具(工具安裝可以參考Protobuf在iOS中的使用小結(jié))。
protoc -I=./ --descriptor_set_out=./basic.desc ./basic.proto
- Charles配置
① 在Charles中選中使用pb格式的請(qǐng)求鏈接财松,選中Viewer Mappings...
protobuf_01.png
② 選中后彈出配置彈窗瘪贱,按圖中所示,依次填選Request Type
辆毡,Message Type
protobuf_02.png
③ 如果Message Type
下拉列表中沒(méi)有目標(biāo)文件菜秦,點(diǎn)擊Open Descriptor Registry
,Adddesc
文件
protobuf_03.png
如上配置完成之后舶掖,請(qǐng)求和響應(yīng)中的pb數(shù)據(jù)就可以完整的展示出來(lái)了球昨。
3.7 常見(jiàn)問(wèn)題
Charles導(dǎo)入根證書(shū)時(shí),鑰匙串無(wú)反應(yīng)眨攘;下載證書(shū)后拖入鑰匙串無(wú)反應(yīng)主慰;雙擊證書(shū)添加提示不能修改鑰匙串;這三種問(wèn)題都可以通過(guò)重置根證書(shū)來(lái)解決鲫售。Charles中點(diǎn)擊Help->SSL Proxying -> Reset Charles Root Certificate
共螺,然后重新添加根證書(shū),設(shè)備添加證書(shū)并信任就可以了情竹。
4. UI調(diào)試
Xcode
自身已經(jīng)提供了App界面調(diào)試工具藐不,運(yùn)行項(xiàng)目,點(diǎn)擊Xocde
的Debug View Hierarchy
按鈕秦效,如圖所示:
可以看到程序當(dāng)前界面的視圖樹(shù)和層級(jí)關(guān)系雏蛮,所有空間的大小、位置一目了然棉安,對(duì)于UI調(diào)試十分有利底扳。但是Xcode提供的這個(gè)工具仍然不夠完美,比如點(diǎn)擊某個(gè)層級(jí)視圖時(shí)不能和左側(cè)的視圖樹(shù)關(guān)聯(lián)起來(lái)贡耽,不能迅速找到想要的視圖信息等等衷模。不過(guò)不用擔(dān)心,確實(shí)還有另一款界面調(diào)試神器等著我們蒲赂,Reveal
!
Reveal
不僅具備展示視圖樹(shù)和層級(jí)關(guān)系的能力阱冶,還允許開(kāi)發(fā)者編輯各種用戶界面參數(shù),結(jié)果還能再用戶界面上實(shí)時(shí)刷新展示出來(lái)滥嘴。部分功能可以在不修改代碼木蹬,不重新運(yùn)行項(xiàng)目的情況下調(diào)試iOS應(yīng)用的App界面。如何安裝配置可以參考這篇文章Reveal使用若皱。
是不是很厲害的一款UI調(diào)試工具镊叁,如果你的iOS設(shè)備越獄了尘颓,它還可以"調(diào)試"其他App的界面,以幫助我們研究學(xué)習(xí)業(yè)界某些精品App的牛X界面效果晦譬。
5. H5調(diào)試
一個(gè)App不可能全由Native
頁(yè)面組成疤苹,可能還會(huì)包含由H5
、React Native
敛腌、Fluttter
等實(shí)現(xiàn)的頁(yè)面卧土,此處我們來(lái)看下如何在App端調(diào)試H5
頁(yè)面。以iOS為例像樊,我們需要Xcode
尤莺、iPhone
(或模擬器)和Safari共同完成調(diào)試工作。
5.1 配置
- 想要使用Safari完成調(diào)試工作生棍,首先要設(shè)置將其設(shè)置為開(kāi)發(fā)模式颤霎。打開(kāi)Safari瀏覽器,在菜單欄選擇
Safari瀏覽器 --> 偏好設(shè)置...
足绅,彈窗中選擇高級(jí)
選項(xiàng)卡捷绑,勾選在菜單欄中顯示“開(kāi)發(fā)”菜單
選項(xiàng)。
-
iPhone
也需要設(shè)置氢妈,打開(kāi)設(shè)置 --> Safari瀏覽器 --> 高級(jí)
粹污,將網(wǎng)頁(yè)檢查器
開(kāi)關(guān)打開(kāi)。
5.2 調(diào)試
- 使用
Xcode
運(yùn)行項(xiàng)目首量,在iPhone
或模擬器上打開(kāi)想要調(diào)試的H5
頁(yè)面壮吩。 - 打開(kāi)Safari瀏覽器,在菜單欄選擇
開(kāi)發(fā) --> iPhone/模擬器 --> 要調(diào)試的H5頁(yè)面
加缘。
- 在彈出的調(diào)試頁(yè)面就可以做調(diào)試工作鸭叙。
6. React Native調(diào)試
如果項(xiàng)目中使用React Native
開(kāi)發(fā)了某些頁(yè)面和模塊,那么React Native
頁(yè)面的調(diào)試也是必不可少的拣宏,下面我們來(lái)看下如果使用Xcode
和Chrome
來(lái)完成React Native
代碼調(diào)試沈贝。
6.1 開(kāi)啟調(diào)試模式
Xcode
運(yùn)行項(xiàng)目打開(kāi)RN頁(yè)面,真機(jī)使用搖一搖勋乾,模擬器使用Command+D
打開(kāi)Developer Menu
宋下,可以看到開(kāi)發(fā)者菜單欄。
-
Reload
辑莫,將項(xiàng)目中的JS
代碼部分重新生成bundle学歧,然后傳輸給真機(jī)或者模擬器。 -
Debug JS Remotely
各吨,使用Chrome遠(yuǎn)程調(diào)試RN代碼枝笨,支持?jǐn)帱c(diǎn)調(diào)試。 -
Enable Live Reload
,顧名思義JS
代碼改動(dòng)后可以實(shí)時(shí)自動(dòng)生成bundle傳遞到真機(jī)或者模擬器上横浑。改完代碼后立馬可以看到效果剔桨,體驗(yàn)更好。 -
Enable Hot Reloading·
徙融,JS代碼改動(dòng)后每次保存的時(shí)候领炫,Hot Reloading功能便會(huì)生成此次修改代碼的增量包,然后傳輸?shù)秸鏅C(jī)或模擬器上實(shí)現(xiàn)熱加載张咳。
6.2 Debug JS Remotely
-
Xcode
中,在React Native
代碼文件似舵,將localhost
改為電腦的IP
地址脚猾。運(yùn)行項(xiàng)目打開(kāi)RN頁(yè)面。
XXReactNativeController *deatilVC = [[self alloc] init];
#if DEBUG
NSURL *jsCodeLocation = [NSURL URLWithString:@"http://10.220.38.114:8081/index.bundle?platform=ios"];
#else
NSURL *jsCodeLocation = [NSURL fileURLWithPath:[DDFileTool rn_proudct_bundlePath]];
#endif
NSMutableDictionary *properties = [NSMutableDictionary dictionary];
properties[@"type"] = @(type);
properties[@"product_id"] = productID;
RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation moduleName:kRNModuleName initialProperties:@{@"nativeData":properties} launchOptions:nil];
deatilVC.view = rootView;
- 在
Developer Menu
選中Debug JS Remotely砚哗,Chrome會(huì)自動(dòng)為我們打開(kāi)遠(yuǎn)程調(diào)試窗口龙助,使用快捷鍵Command+Option+I
打開(kāi)開(kāi)發(fā)者工具。
打開(kāi)開(kāi)發(fā)者工具后即可看到上面圖片內(nèi)容蛛芥,中間是你的本地RN文件提鸟,右側(cè)是代碼調(diào)試區(qū)。在右邊區(qū)域選中Sources
選項(xiàng)卡就可以看到所要檢查RN頁(yè)面的所有代碼仅淑,并且整個(gè)面板提供了暫停称勋、恢復(fù)、步進(jìn)等調(diào)試功能以及控制臺(tái)log輸出區(qū)域涯竟。具體功能細(xì)節(jié)如圖所示:
如果還想了解更多調(diào)試技巧赡鲜,可以參考React Native調(diào)試技巧與心得這篇文章。
7. Flutter調(diào)試
Flutter
調(diào)試在上篇文章Flutter混編方案已經(jīng)介紹過(guò)了庐船,有興趣的同學(xué)可以移步去了解一下银酬。