報錯警告調試
你在實際開發(fā)中,有哪些手機架構與性能調試經驗
- 剛接手公司的舊項目時练湿,模塊特別多妖碉,而且?guī)缀跛械拇a都寫在控制器里面涌庭,比如UI控件代碼、網絡請求代碼欧宜、數據存儲代碼
- 接下來采取MVC模式進行封裝坐榆、重構
- 自定義UI控件封裝內部的業(yè)務邏輯
- 封裝網絡請求工具類(降低耦合)
- 封裝數據存儲工具類
BAD_ACCESS在什么情況下出現(xiàn)?
這種問題是經常遇到的冗茸,在開發(fā)時經常會出現(xiàn)BAD_ACCESS席镀。原因是訪問了野指針匹中,比如訪問已經釋放對象的成員變量或者發(fā)消息、死循環(huán)等豪诲。
如何調試BAD_ACCESS錯誤顶捷?
出現(xiàn)BAD_ACCESS錯誤,通常是訪問了野指針屎篱,比如訪問了已經釋放了的對象服赎。快速定位問題的步驟有:
1. 重寫對象的respondsToSelector方法交播,先找到出現(xiàn)EXECBADACCESS前訪問的最后一個object
2. 設置Enable Zombie Objects
3. 設置全局斷點快速定位問題代碼所在行重虑,接收所有的異常
4. Xcode7已經集成了BAD_ACCESS捕獲功能:Address Sanitizer,與步驟2一樣設置
5. analyze也行(不一定管用)
什么時候會報 unrecognized selector 異常秦士?
當調用對象(子類缺厉,各級父類)中不含有對應方法的時候,并且依舊沒有給出“消息轉發(fā)”的具體方案的時候隧土,程序在運行時會crash并拋出 unrecognized selector 異常
objective-c 中的每個方法在運行時會被轉為消息發(fā)送objc_msgSend(reciver, selector)
例如 [person say]就會被轉化為 objc_msgSend(person, @selector(say))
運行時會根據對象(reciever) 的isa 指針找到該對象所對應的類芽死,然后會依次在對應的類,父類次洼,爺爺類,根類中找對應的方法
下面只講述對象方法的解析過程:
- 第一步:+ (BOOL)resolveInstanceMethod:(SEL)sel實現(xiàn)方法遇骑,指定是否動態(tài)添加方法卖毁。若返回NO,則進入下一步落萎,若返回YES亥啦,則通過class_addMethod函數動態(tài)地添加方法,消息得到處理练链,此流程完畢翔脱。
- 第二步:在第一步返回的是NO時,就會進入- (id)forwardingTargetForSelector:(SEL)aSelector方法媒鼓,這是運行時給我們的第二次機會届吁,用于指定哪個對象響應這個selector。不能指定為self绿鸣。若返回nil疚沐,表示沒有響應者,則會進入第三步潮模。若返回某個對象亮蛔,則會調用該對象的方法。
- 第三步:若第二步返回的是nil擎厢,則我們首先要通過- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector指定方法簽名究流,若返回nil辣吃,則表示不處理。若返回方法簽名芬探,則會進入下一步神得。
- 第四步:當第三步返回方法方法簽名后,就會調用- (void)forwardInvocation:(NSInvocation *)anInvocation方法灯节,我們可以通過anInvocation對象做很多處理循头,比如修改實現(xiàn)方法,修改響應對象等
- 第五步:若沒有實現(xiàn)- (void)forwardInvocation:(NSInvocation *)anInvocation方法炎疆,那么會進入- (void)doesNotRecognizeSelector:(SEL)aSelector方法卡骂。若我們沒有實現(xiàn)這個方法,那么就會crash形入,然后提示打不到響應的方法全跨。到此,動態(tài)解析的流程就結束了亿遂。
有哪些常見的 Crash 場景浓若?
- 訪問了僵尸對象
- 訪問了不存在的方法
- 數組越界
- 在定時器下一次回調前將定時器釋放,會Crash
lldb(gdb)常用的調試命令?
? p 輸出基本類型//p (int)[[[self view] subviews] count]
? po 用于輸出 Objective-C 對象//po [self view]
? expr 可以在調試時動態(tài)執(zhí)行指定表達式蛇数,并將結果打印出來挪钓。常用于在調試過程中修改變量的值。//源代碼中 a = 1 耳舅;expr a=2 輸出結果:(int) $0 = 2
如果一個函數10次中有7次正確碌上,3次錯誤,問題可能出現(xiàn)在哪里浦徊?
這樣的問題通過應聘者的分析馏予,可以知道應聘者的功底如何。很多人的回答會是很簡單的盔性,沒有從多方面去分析霞丧。這樣的問題也是很有意義的,在項目開發(fā)中所產生的bug冕香,有的時候會出現(xiàn)這樣的情況蛹尝,而代碼量比較大且業(yè)務比較復雜時,通過其他工具并不能分析出來是什么bug暂筝,但是我們卻可以根據出現(xiàn)的頻率推測箩言。筆者把這個問題當作測試部反饋過來的bug描述問題來分析一下。
參考答案:
從問題描述可知焕襟,bug不會必現(xiàn)的陨收,因此無法直接定位出錯之處。從以下角度出現(xiàn)來分析可能出錯之處:
1. 因出錯并不是崩潰,因此沒有錯誤日志可看务漩。第一步就是分析函數中的所有分支拄衰,是否在語法上存在可能缺少條件的問題。所以饵骨,檢查所有的分支翘悉,確保每個分支執(zhí)行的結果的正確的
2. 檢測函數的參數,保證必傳參數不能為空居触,若為空應該拋出異常妖混。因此,用斷言檢測參數的正確性是很重要的轮洋。
3. 檢測函數中每個分支所調用的函數返回結果是正確的制市,其實就是一個遞歸的過程(步驟1、2)
你一般是如何調試Bug的弊予?
這個問題看起來很籠統(tǒng)祥楣,但又一針見血。通過應聘者的回答汉柒,可很直觀地看出這個應聘者的處理bug的能力误褪,以及其解決問題的思維。
參考答案:
Bug分為測試中的Bug和線上的Bug:
? 線上Bug:項目使用了友盟統(tǒng)計碾褂,因此會有崩潰日志兽间,通過解析dYSM可以直接定位到大部分bug崩潰之處。解決線上bug需要從主干拉一個新的分支正塌,解決bug并測試通過后渡八,再合并到主干,然后上線传货。若是多團隊開發(fā),可以將fix bug分支與其他團隊最近要上線的分支集成宏娄,然后集成測試再上線问裕。
? 測試Bug:根據測試所反饋的bug描述,若語義不清晰孵坚,則直接找到提bug人粮宛,操作給開發(fā)人員看,最好是可以bug復現(xiàn)卖宠。解決bug時巍杈,若能根據描述直接定位bug出錯之處,則好處理扛伍;若無法直觀定位筷畦,則根據bug類型分幾種處理方式,比如崩潰的bug可以通過instruments來檢測、數據顯示錯誤的bug鳖宾,則需要閱讀代碼一步步查看邏輯哪里寫錯吼砂。
對于開發(fā)中出現(xiàn)的崩潰或者數據顯示不正常,那就需要根據經驗或者相關工具來檢測可能出錯之處鼎文。當然渔肩,團隊內溝通解決是最好的。
獲取一臺設備唯一標識的方法有哪些拇惋?
現(xiàn)在常用的是用UUID + keychain結合來實現(xiàn)這個需求周偎。
UUID是Universally Unique Identifier的縮寫,中文意思是通用唯一識別碼撑帖。它是讓分布式系統(tǒng)中的所有元素蓉坎,都能有唯一的辨識資訊,而不需要透過中央控制端來做辨識資訊的指定磷仰。這樣袍嬉,每個人都可以建立不與其它人沖突的 UUID。在此情況下灶平,就不需考慮數據庫建立時的名稱重復問題伺通。蘋果公司建議使用UUID為應用生成唯一標識字符串。
//獲取一個UUID
- (NSString*)uuid {
CFUUIDRef uuid = CFUUIDCreate( nil );
CFStringRef uuidString = CFUUIDCreateString( nil, uuid );
NSString * result = (NSString *)CFBridgingRelease(CFStringCreateCopy( NULL, uuidString));
CFRelease(uuid);
CFRelease(uuidString);
return result;
}
現(xiàn)在我們獲取到了一個UUID逢享,雖然這個標識是唯一的罐监,但是這樣還是無法保證每一次的唯一性,因為當你每次調用這個方法或者把應用卸載了瞒爬,UUID會重新生成一個不同的弓柱。這個時候keychain就起到了作用。
所以整個邏輯是這樣的:先從keychain取UUID侧但,如果能取到矢空,就用這個比對,如果取不到就重新生成一個保存起來禀横。keychain獨立在App之外屁药,是和系統(tǒng)統(tǒng)一等級的,所以你不用擔心它掛掉柏锄。
keychain是蘋果公司Mac OS中的密碼管理系統(tǒng)酿箭。它在Mac OS 8.6中被導入秆撮,并且包括在了所有后續(xù)的Mac OS版本中准验,包括Mac OS X。一個鑰匙串可以包含多種類型的數據:密碼(包括網站惜颇,F(xiàn)TP服務器抬闷,SSH帳戶妇蛀,網絡共享,無線網絡,群組軟件讥耗,加密磁盤鏡像等)有勾,私鑰,電子證書和加密筆記等古程。iOS端同樣有個keychain幫助我們管理這些敏感信息蔼卡。
使用過keychain保存過賬號密碼的童鞋應該對這個工具非常了解,在這里不做過多解釋挣磨。使用keychain需要導入Security.framework和KeychainItemWrapper.h/.m雇逞,KeychainItemWrapper.h/.m搜一下可以下載下來,拖入工程中茁裙。保存UUID代碼如下:
- (void)saveUuidWithKeyChain {
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]
initWithIdentifier:@"UUID" accessGroup:@"com.xxx.www"];
NSString *strUUID = [keychainItem objectForKey:(id)kSecValueData];
if (strUUID == nil || [strUUID isEqualToString:@""])
{
[keychainItem setObject:[self uuid] forKey:(id)kSecValueData];
}
}
注:這個方法中accessGroup:這個參數如果一些App設置相同的話塘砸,是可以共享的。
- 從keychain獲取UUID的方法如下:
- (NSString *)getKeychain {
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc]
initWithIdentifier:@"UUID" accessGroup:@"com.xxx.www"];
NSString *strUUID = [keychainItem objectForKey:(id)kSecValueData];
return strUUID;
}
至此晤锥,基本上唯一標識的幾個方法算是寫完了掉蔬,大家可以測試一下,卸載應用再重新裝矾瘾,從keychain讀取的UUID還是和之前一樣女轿。
但這里有個不確定因素,就是手機系統(tǒng)恢復出廠設置或者抹掉所有數據的話壕翩,這個方法也可能不起作用了蛉迹,因為它是依靠鑰匙串在生存,鑰匙串掛掉的話它也就失效了放妈。
你一般是怎么用 Instruments 的北救?
這個問題也就是考察下你經驗如何了, Instruments里面工具很多,也沒必要逐一說明,挑幾個常用的說下就好
參考答案:
Time Profiler:性能分析
Zombies:檢查是否訪問了僵尸對象,但是這個工具只能從上往下檢查,不智能
Allocations:用來檢查內存,寫算法的那批人也用這個來檢查
Leaks:檢查內存,看是否有內存泄露
你一般是如何調試 Bug 的?
- 查看異常報告
- 配置相關環(huán)境芜抒,重現(xiàn)bug
- 代碼檢查
- 用測試案例來捕獲bug
- 可以請同事一同來審查問題珍策,有些時候當局者迷,旁觀者清宅倒。
如何對iOS設備進行性能測試?
Profile-> Instruments ->Time Profiler 進行性能測試膛壹!
測試iOS版的 App 注意事項分享以下幾點:
1.app使用過程中,接聽電話唉堪。可以測試不同的通話時間的長短肩民,對于通話結束后唠亚,原先打開的app的響應,比如是否停留在原先界面持痰,繼續(xù)操作時的相應速度等灶搜。
2.app使用過程中,有推送消息時,對app的使用影響
3.設備在充電時割卖,app的響應以及操作流暢度
4.設備在不同電量時(低于10%前酿,50%,95%)鹏溯,app的響應以及操作流暢度
5.意外斷電時罢维,app數據丟失情況
6.網絡環(huán)境變化時,app的應對情況如何:是否有適當提示?從有網絡環(huán)境到無網絡環(huán)境時丙挽,app的反饋如何?從無網絡環(huán)境回到有網絡環(huán)境時肺孵,是否能自動加載數據,多久才能開始加載數據
7.多點觸摸的情況
8.跟其他app之間互相切換時的響應
9.進程關閉再重新打開的反饋
10.IOS系統(tǒng)語言環(huán)境變化時
文章如有問題颜阐,請留言平窘,我將及時更正。
滿地打滾賣萌求贊凳怨,如果本文幫助到你瑰艘,輕點下方的紅心,給作者君增加更新的動力肤舞。