前言
眾所周知,Bug是線上應(yīng)用極力規(guī)避但又無(wú)法避免的。對(duì)于致命的Bug,我們可以通過(guò)Crash日志進(jìn)行分析;對(duì)于無(wú)法復(fù)現(xiàn)的Bug、特定操作步驟引起的Bug、某些版本/系統(tǒng)才出現(xiàn)的Bug,每個(gè)開發(fā)者都有自己的一套分析、定位、解決的方法。
本文以工作中遇到的幾個(gè)iOS 11 Bug為例,介紹快速定位、分析、解決Bug的經(jīng)驗(yàn)。
正文
iOS 11裁剪圖片偏移問(wèn)題
功能背景:
用戶從本地相冊(cè)選擇圖片,然后裁剪一個(gè)正方形區(qū)域,最后生成用戶頭像。
Bug描述:
iOS 11的iPhone X,選擇本地圖片,然后進(jìn)行裁剪,生成的圖片有明顯的偏移,如下:
功能實(shí)現(xiàn):
裁剪控件是系統(tǒng)提供的UIImagePickerController。
Bug定位:
用模擬器進(jìn)行復(fù)現(xiàn),并斷點(diǎn)在UIImagePickerController的回調(diào)方法,再用Xcode查看實(shí)際的返回參數(shù)和圖片。
如下:
①是特意選擇的區(qū)域,剛好覆蓋到瀑布的頂部;
②是UIImagePickerController的返回參數(shù),通過(guò)po命令查看;
③是按照返回參數(shù)的CropRect在原圖截取出來(lái)的區(qū)域;
④是返回參數(shù)中的圖片;
經(jīng)過(guò)可以對(duì)比發(fā)現(xiàn),③和④的圖片是一致的,并且明顯與①所選中的區(qū)域有所偏移。以同樣的方式嘗試iPhone X和6s的模擬器,發(fā)現(xiàn)都有偏移現(xiàn)象,且iPhone X的偏移更為嚴(yán)重。
檢查本地代碼,確認(rèn)是正常的方式調(diào)用UIImagePickerController,那具體是哪一步影響裁剪結(jié)果呢?
仔細(xì)體驗(yàn)UIImagePickerController的裁剪功能,發(fā)現(xiàn)一個(gè)可疑的現(xiàn)象:
6s模擬器,藍(lán)色箭頭指向的區(qū)域是無(wú)法選擇的!
以這個(gè)區(qū)域?yàn)橥黄瓶冢瑢?duì)比此處區(qū)域的高度值和裁剪的偏移值,得到大致是1:2的比例,符合2x屏幕。
用iPhone X模擬器同樣復(fù)現(xiàn)了這個(gè)問(wèn)題,并且不能選擇的區(qū)域更大。
而且非常有意思的是:iPhone X模擬器的裁剪偏移量為44pixel。
對(duì)于做過(guò)iPhone X適配的開發(fā),對(duì)于44這個(gè)數(shù)值域是非常敏感的(頂部安全區(qū)域的高度),猜測(cè)是和statusBar有關(guān)。
再找到6s的模擬器對(duì)比裁剪偏移量,果不其然,大致是22pixel。
至此,Bug摸清來(lái)龍去脈:
UIImagePickerController的裁剪選擇視圖向下偏移了status bar的高度,但是裁剪的時(shí)候還是按照y=0計(jì)算,導(dǎo)致結(jié)果產(chǎn)生偏移。(猜測(cè)是iOS 11 UIScrollView的contentInsetAdjustmentBehavior屬性導(dǎo)致)
Bug解決:
裁剪時(shí),隱藏statusBar。
PS:此Bug在iOS8也會(huì)出現(xiàn),iOS 9/10是正常的。
iOS 8隱藏statusBar需要在UIImagePickerController的delegate實(shí)現(xiàn)中,添加以下代碼
- (void)navigationController:(UINavigationController *)navigationController
willShowViewController:(UIViewController *)viewController
animated:(BOOL)animated {
if ([navigationController isKindOfClass:[UIImagePickerController class]] ) {
[[UIApplication sharedApplication] setStatusBarHidden:YES];
}
}
小結(jié):
善用工具,快速定位。
對(duì)于能夠復(fù)現(xiàn)的Bug,Xcode連接真機(jī)斷點(diǎn)調(diào)試是最方便的方法。
但是切記,不要沉浸在單步調(diào)試和盲目枚舉嘗試的過(guò)程。
iOS 11圖像放大閃爍問(wèn)題
功能背景:
用戶點(diǎn)擊圓形頭像后,頭像會(huì)放大到等同屏幕寬度,并且從圓形展示變成正方形展示。
Bug描述:
iOS 11的iPhone 7p,在點(diǎn)擊頭像之后,在頭像放大的過(guò)程中會(huì)有閃爍的現(xiàn)象。(iPhone X效果最為嚴(yán)重,除了閃爍還有抖動(dòng)現(xiàn)象)
功能實(shí)現(xiàn):
圓角按鈕通過(guò)layer.cornerRadius實(shí)現(xiàn),頭像放大是UIView的animation block動(dòng)畫;
Bug定位:
先用模擬器進(jìn)行嘗試,發(fā)現(xiàn)無(wú)法復(fù)現(xiàn);再用真機(jī)進(jìn)行測(cè)試,發(fā)現(xiàn)偶然會(huì)閃爍的現(xiàn)象。
用錄屏工具輔助,定位到閃爍是因?yàn)閳D片放大的動(dòng)畫過(guò)程中,出現(xiàn)了某一幀異常:
上面的展示效果類似OpenGL紋理展示的GL_CLAMP_TO_EDGE模式,懷疑是圖像放大過(guò)程中的邊界處理有異常。
帶著疑問(wèn)回看代碼。查看頭像詳情時(shí),點(diǎn)擊頭像(為圓形)會(huì)全屏顯示頭像大圖。整個(gè)過(guò)程的動(dòng)畫內(nèi)容包括兩個(gè):
1、imageView的frame變成覆蓋整個(gè)屏幕;
2、imageView的layer.cornerRadius變成0;
以上的代碼,在iOS 10下沒(méi)有閃爍問(wèn)題,但是iOS 11就會(huì)出現(xiàn)這個(gè)異常。
遇到代碼不同iOS版本的表現(xiàn)不同時(shí),先查一下API的變動(dòng)。
查看蘋果的文檔后發(fā)現(xiàn),layer的cornerRadius屬性在iOS 11之前是不支持Block動(dòng)畫的。iOS 11之后新增了cornerRadius屬性的Block動(dòng)畫支持,但是明顯支持效果不是很好。
Bug解決:
解決方案1:移除動(dòng)畫過(guò)程中cornerRadius的屬性變化;
解決方案2:統(tǒng)一用CoreAnimation來(lái)實(shí)現(xiàn);
小結(jié):
模擬器先行,真機(jī)驗(yàn)證。
模擬器具備多開的優(yōu)勢(shì),可以同時(shí)打開多個(gè)系統(tǒng)的多個(gè)設(shè)備;但是因?yàn)槟M器的cpu架構(gòu)與真機(jī)不同,最終必須用真機(jī)驗(yàn)證。
文檔為主,Google為輔。
iOS版本升級(jí)經(jīng)常引入Bug,對(duì)于這種不同iOS系統(tǒng)導(dǎo)致的問(wèn)題,需要查看文檔(文檔包括Xcode的頭文件以及自帶的文檔),如果文檔找不到則用Google查找對(duì)應(yīng)的關(guān)鍵詞。
iOS 11動(dòng)畫異常問(wèn)題
功能背景:
正常的動(dòng)畫效果,比如微信的聊天圖片放大動(dòng)畫和手Q的頭像放大動(dòng)畫,如下圖:
Bug描述:
動(dòng)畫與正常有異,最明顯是出現(xiàn)這個(gè)情況:
上圖的三角形區(qū)域應(yīng)該是如下的區(qū)域:
功能實(shí)現(xiàn):
猜測(cè)是用UIView的Block動(dòng)畫,或者CoreAnimation實(shí)現(xiàn)。
Bug定位:
Bug不限于普通app,在系統(tǒng)app也會(huì)出現(xiàn)這種異常,表現(xiàn)形式為:頁(yè)面切換卡頓、動(dòng)畫執(zhí)行異常。
Debug調(diào)試開發(fā)中發(fā)現(xiàn),動(dòng)畫的animationBlock和completionBlock的調(diào)用時(shí)序是正常的。以頭像縮小的動(dòng)畫為例,以下是正常的動(dòng)畫時(shí)序:
從動(dòng)畫的異常表現(xiàn)上猜測(cè),原因是動(dòng)畫延遲執(zhí)行。
嘗試在completionBlock中改變背景顏色,可以看出動(dòng)畫還在執(zhí)行時(shí),背景顏色發(fā)生了變化;
嘗試在動(dòng)畫開始改變視圖顏色,可以發(fā)現(xiàn)動(dòng)畫執(zhí)行存在明顯的延遲;
可以確定:當(dāng)發(fā)生這個(gè)錯(cuò)誤之后,動(dòng)畫的執(zhí)行實(shí)現(xiàn)會(huì)推遲,導(dǎo)致completionBlock調(diào)用的時(shí)候動(dòng)畫仍在執(zhí)行,產(chǎn)生異常的現(xiàn)象。
用下面的時(shí)序圖來(lái)描述:在第10s提交一個(gè)0.2s的動(dòng)畫,動(dòng)畫執(zhí)行完畢的時(shí)間是10.5s左右(正常應(yīng)該是10.2s),動(dòng)畫延遲時(shí)間在0.2~0.4s區(qū)間。
通過(guò)KVO觀察layer的frame和presentationLayer的frame,整個(gè)動(dòng)畫過(guò)程的調(diào)用也是正常。
在模型樹=>呈現(xiàn)樹=>渲染樹這條鏈路上,開發(fā)者通過(guò)代碼層面上只能獲取到前兩個(gè)環(huán)境的數(shù)據(jù),至此問(wèn)題停止深入。
只能把Bug總結(jié)為:iOS 11系統(tǒng)的手機(jī)在某些情況下會(huì)發(fā)生系統(tǒng)錯(cuò)誤,導(dǎo)致整個(gè)手機(jī)的動(dòng)畫機(jī)制出現(xiàn)異常。
Bug出現(xiàn)之后,無(wú)法通過(guò)代碼修復(fù)(iOS系統(tǒng)錯(cuò)誤),只能重啟手機(jī)。
Bug解決:
提示用戶重啟手機(jī)(可暫時(shí)修復(fù));
向蘋果提交Bug。
小結(jié):
對(duì)于某些所有APP都存在的異常現(xiàn)象,歸類為系統(tǒng)級(jí)Bug,可以在developer.apple.com的Bug Reporter提交Bug。
猜測(cè)、定位到問(wèn)題所在之后,可以嘗試修復(fù),但是此Bug不在此列,不建議花費(fèi)過(guò)多精力。
iOS 11 下拉刷新異常問(wèn)題
功能背景:
在某些頁(yè)面中,存在下拉刷新/上拉加載更多的功能。
Bug描述:
iOS 11的手機(jī),在下拉刷新之后,會(huì)一直處于“加載中”的狀態(tài)。
功能實(shí)現(xiàn):
通過(guò)KVO監(jiān)聽tableView的屬性,并判斷具體的操作,最終通過(guò)自定義的dragDelegate回調(diào)。
Bug定位:
通過(guò)模擬器復(fù)現(xiàn),發(fā)現(xiàn)iOS 10的模擬器正常,iOS 11的模擬器存在此問(wèn)題。
在后臺(tái)數(shù)據(jù)返回的接口處斷點(diǎn),確定數(shù)據(jù)返回是否正常,發(fā)現(xiàn)iOS 11的模擬器根本沒(méi)有返回?cái)?shù)據(jù)。
據(jù)此回溯定位:后臺(tái)數(shù)據(jù)沒(méi)有返回=>客戶端沒(méi)發(fā)協(xié)議=>下拉刷新沒(méi)有回調(diào)。
再?gòu)腢IScrollView的delegate回調(diào)入手,單步調(diào)試定位到問(wèn)題:
UITableView iOS 11新增了一個(gè)屬性:dragDelegate,與開發(fā)者自定義的dragDelegate沖突!
Bug解決:
修改屬性名,同時(shí)注意以后在給系統(tǒng)的類添加屬性時(shí),需要加上自己的方法名前綴,防止與系統(tǒng)沖突。
小結(jié):
正向和逆向的鏈?zhǔn)椒治龇绞绞亲畛R姷腂ug定位方法;
對(duì)于實(shí)在無(wú)法定位時(shí),再使用二分注釋的方法。
總結(jié)
iOS 11更新之后出現(xiàn)的問(wèn)題比以往的版本更多,要求開發(fā)者需要投入更多的精力去適配,甚至?xí)嬖谝恍╅_發(fā)者無(wú)法修復(fù)的Bug。為了iPhone X全新的交互體驗(yàn),iOS 11做了非常大的改動(dòng),之前的beta版本問(wèn)題更多。
善用Xcode的調(diào)試工作,勤看具體API的頭文件,遇到問(wèn)題仔細(xì)分析。
無(wú)需對(duì)Bug產(chǎn)生厭惡和恐懼,但也不要沉浸在單步調(diào)試和盲目嘗試的快感中。充分利用寶貴的時(shí)間,減少無(wú)用的步驟,覺察自己解決Bug過(guò)程中的不足,盡量從解決問(wèn)題中習(xí)得新的知識(shí)和方法。