轉(zhuǎn)發(fā)自
http://blog.csdn.net/skylin19840101/article/details/51500900
謝謝大佬
質(zhì)量問(wèn)題不僅僅是商品應(yīng)該注重的,在移動(dòng)互聯(lián)占據(jù)人們各個(gè)生活領(lǐng)域的前提下清笨,產(chǎn)品質(zhì)量更顯重要摩疑,以最具人氣和潛力的iOS為例,iOS從系統(tǒng)研發(fā)和客戶端軟件開(kāi)發(fā)環(huán)節(jié)對(duì)質(zhì)量的要求異常高三圆,在注重用戶體驗(yàn)的同時(shí)提升產(chǎn)品質(zhì)量,這也是很多用戶非iOS不用的原因,iOS系統(tǒng)已經(jīng)讓移動(dòng)互聯(lián)網(wǎng)的品質(zhì)得到升級(jí)博肋。那么我們?cè)陂_(kāi)發(fā)iOS產(chǎn)品時(shí),如何提高它的質(zhì)量呢蜂厅?
涉及到質(zhì)量問(wèn)題匪凡,這就是一個(gè)很大的話題,包括很多方面掘猿,比如代碼書寫的質(zhì)量病游,開(kāi)發(fā)流程的規(guī)范,項(xiàng)目管理的到位,測(cè)試的最后把關(guān)等各個(gè)環(huán)節(jié)衬衬。編碼需要規(guī)范买猖,命名需要有意義;接口低耦合滋尉、高內(nèi)聚玉控、易擴(kuò)展,代碼能重用狮惜、避免重復(fù)代碼高诺;提交代碼后需要做CodeReview;Release前碾篡,自測(cè)需要充分虱而,包括單元測(cè)試、和其他模塊(服務(wù)器)的聯(lián)調(diào)測(cè)試开泽,網(wǎng)絡(luò)性能測(cè)試牡拇,不同機(jī)型、不同系統(tǒng)版本穆律、越獄與否等各個(gè)方面的測(cè)試惠呼;通過(guò)冒煙測(cè)試后才交于QA測(cè)試等等。在這里众旗,我們主要分享從開(kāi)發(fā)人員的角度如何提高產(chǎn)品質(zhì)量罢杉,包含的內(nèi)容如下:
<a name="t0" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>避免異常:
從上面可以看到,iOS開(kāi)發(fā)中常見(jiàn)的異常包括以下幾種:
NSInvalidArgumentException
NSRangeException
NSGenericException
NSInternalInconsistencyException
NSFileHandleOperationException
<a name="t1" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>NSInvalidArgumentException
非法參數(shù)異常(NSInvalidArgumentException)是 Objective - C 代碼最常出現(xiàn)的錯(cuò)誤贡歧,所以平時(shí)在寫代碼的時(shí)候滩租,需要多加注意,加強(qiáng)對(duì)參數(shù)的檢查利朵,避免傳入非法參數(shù)導(dǎo)致異常律想,其中尤以nil參數(shù)為甚。
<a name="t2" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>集合數(shù)據(jù)的參數(shù)傳遞
比如NSMutableArray, NSMutableDictionary的數(shù)據(jù)操作
(1) 不能刪除nil的key(2) NSDictionary不能添加nil的對(duì)象
(3) 不能插入nil的對(duì)象
[圖片上傳失敗...(image-ca0fe2-1515303094173)]
(4) 其他一些nil參數(shù)
<a name="t3" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>其他一些API的使用
<a name="t4" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
(1) NSDictionary不能刪除nil的key
<a name="t5" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
APP一般都會(huì)有網(wǎng)絡(luò)操作绍弟,免不了使用網(wǎng)絡(luò)相關(guān)接口技即,比如NSURL的初始化,不能傳入nil的http地址:
<a name="t6" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>未實(shí)現(xiàn)的方法
(1) .h文件里修改了函數(shù)名樟遣,卻忘了修改.m文件里對(duì)應(yīng)的函數(shù)名而叼;或者頭文件里定義了函數(shù),.m文件里沒(méi)有實(shí)現(xiàn)
(2) 使用第三方庫(kù)時(shí)豹悬,沒(méi)有添加”-ObjC” flag
(3) MRC時(shí)葵陵,大部分情況下是因?yàn)閷?duì)象被提前release了,在你心里不希望他release的情況下瞻佛,指針還在脱篙,對(duì)象已經(jīng)不在了。 比如:
<a name="t7" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
(4) 對(duì)象類型使用不當(dāng)
[圖片上傳失敗...(image-af0fee-1515303094173)]
因?yàn)閕magenS本來(lái)是NSDictionary的對(duì)象,被當(dāng)做NSString來(lái)處理了
類似的绊困,NSDictionary的對(duì)象被當(dāng)做NSArrary來(lái)處理了
<a name="t8" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a> NSRangeException
越界異常(NSRangeException)也是比較常出現(xiàn)的異常文搂,有如下幾種類型:
<a name="t9" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
<a name="t10" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>數(shù)組最大下標(biāo)處理錯(cuò)誤
比如數(shù)組長(zhǎng)度count, index的下標(biāo)范圍[0, count -1], 在開(kāi)發(fā)時(shí),可能index的最大值超過(guò)數(shù)組的范圍秤朗;
<a name="t11" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
<a name="t12" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>下標(biāo)的值是其他變量賦值
這樣會(huì)有很大的不確定性煤蹭, 可能是一個(gè)很大的整數(shù)值
這里的值達(dá)到32位和64位整數(shù)的最大值,肯定是一個(gè)不正常的參數(shù)川梅,比如:
如果找不到str 疯兼,則返回NSNotFound然遏,32位下它就是2147483647贫途,64位下18446744073709551615 ,將它作為參數(shù)傳遞則會(huì)導(dǎo)致NSRangeException待侵。
<a name="t13" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
<a name="t14" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>使用空數(shù)組
如果一個(gè)數(shù)組剛剛初始化丢早,還是空的,就對(duì)它進(jìn)行相關(guān)操作
[圖片上傳失敗...(image-4a5ea9-1515303094176)]
所以秧倾,為了避免NSRangeException的發(fā)生怨酝,必須對(duì)傳入的index參數(shù)進(jìn)行合法性檢查,是否在集合數(shù)據(jù)的個(gè)數(shù)范圍內(nèi)那先。
<a name="t15" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>
NSGenericException
NSGenericException這個(gè)異常最容易出現(xiàn)在foreach操作中农猬,在for in循環(huán)中如果修改所遍歷的數(shù)組,無(wú)論你是add或remove售淡,都會(huì)出錯(cuò)斤葱,比如:
執(zhí)行上面的代碼會(huì)出現(xiàn)以下的錯(cuò)誤:
原因就在這 "for in",它的內(nèi)部遍歷使用了類似 Iterator進(jìn)行迭代遍歷,一旦元素變動(dòng)揖闸,之前的元素全部被失效揍堕,所以在foreach的循環(huán)當(dāng)中,最好不要去進(jìn)行元素的修改動(dòng)作汤纸,若需要修改衩茸,循環(huán)改為for遍歷,由于內(nèi)部機(jī)制不同贮泞,不會(huì)產(chǎn)生修改后結(jié)果失效的問(wèn)題楞慈。
[圖片上傳失敗...(image-c8a4bb-1515303094173)]
<a name="t16" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>NSMallocException
這也是內(nèi)存不足的問(wèn)題,無(wú)法分配足夠的內(nèi)存空間
<a name="t17" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>NSFileHandleOperationException
處理文件時(shí)的一些異常啃擦,最常見(jiàn)的還是存儲(chǔ)空間不足的問(wèn)題囊蓝,比如應(yīng)用頻繁的保存文檔,緩存資料或者處理比較大的數(shù)據(jù):[圖片上傳失敗...(image-ec2fa3-1515303094176)]
所以在文件處理里议惰,需要考慮到手機(jī)存儲(chǔ)空間的問(wèn)題慎颗。
<a name="t18" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>二、錯(cuò)誤處理
在進(jìn)行函數(shù)調(diào)用的時(shí)候,如果有NSError的參數(shù)傳遞俯萎,最好都處理以下傲宜,特別是一些網(wǎng)絡(luò)和文件的處理,比如:
文件操作錯(cuò)誤
比如在刪除文件時(shí)夫啊,有可能發(fā)生錯(cuò)誤函卒,我們需要做一些處理
[圖片上傳失敗...(image-60990c-1515303094172)]
網(wǎng)絡(luò)處理錯(cuò)誤
在使用NSURLConnection進(jìn)行網(wǎng)絡(luò)請(qǐng)求時(shí),發(fā)生錯(cuò)誤是難免的撇眯,必須做相應(yīng)處理
<a name="t19" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>三报嵌、本地緩存的兼容處理
在APP的運(yùn)行過(guò)程中,難免會(huì)有I/O的操作熊榛,一般的情況锚国,從技術(shù)的角度來(lái)看也不會(huì)出什么問(wèn)題,但是業(yè)務(wù)邏輯上可能就需要特別注意了玄坦,比如APP升級(jí)版本后血筑,可能保存緩存數(shù)據(jù)的格式發(fā)生變化了,讀取數(shù)據(jù)后的處理邏輯也就相應(yīng)發(fā)生變化煎楣,這時(shí)就需要考慮兼容舊版本數(shù)據(jù)的處理,否則對(duì)升級(jí)用戶了說(shuō)豺总,很可能由于異常發(fā)生崩潰。
<a name="t20" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>四择懂、解析網(wǎng)絡(luò)數(shù)據(jù)
客戶端開(kāi)發(fā)時(shí)喻喳,對(duì)接收到的服務(wù)器數(shù)據(jù),需要特別小心困曙,因?yàn)榻?jīng)過(guò)網(wǎng)絡(luò)傳輸回來(lái)的數(shù)據(jù)都是不可信的表伦,什么問(wèn)題都可能發(fā)生,比如協(xié)議中該有的字段沒(méi)有赂弓,是整數(shù)型的字段結(jié)果傳了一個(gè)子串绑榴,Json格式的內(nèi)容變成xml了…,所以我們?cè)诮馕龇?wù)器數(shù)據(jù)時(shí)盈魁,需要有充分的異常處理機(jī)制翔怎。
<a name="t21" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>五、代碼靜態(tài)檢查
在開(kāi)發(fā)完成后杨耙,需要對(duì)代碼進(jìn)行靜態(tài)檢查赤套,這樣能發(fā)現(xiàn)一些內(nèi)存泄露以及一些warning。
<a name="t22" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>內(nèi)存泄露
<a name="t23" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a> MRC
在MRC的時(shí)候珊膜,內(nèi)存泄露是個(gè)大問(wèn)題容握,一不小心就會(huì)中招
[圖片上傳失敗...(image-699c34-1515303094175)]
注意上面的代碼并不是L63行存在泄漏,我們點(diǎn)擊“Potential leak of an object”前面的箭頭车柠,指示會(huì)出現(xiàn)一些變化剔氏,如下圖塑猖。alloc一個(gè)對(duì)象的時(shí)候,其內(nèi)存計(jì)數(shù)內(nèi)存計(jì)數(shù)(retain count)+1
因?yàn)閏ontent的setter方發(fā)會(huì)將object的內(nèi)存計(jì)數(shù)+1谈跛,如下代碼羊苟,content是retain屬性。執(zhí)行完L62代碼后感憾,self.content的內(nèi)存計(jì)數(shù)就為 2
建議修改方案:
<a name="t24" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>ARC
在ARC下蜡励,這方面的問(wèn)題就少很多了,但也還是會(huì)出現(xiàn)的阻桅,比如:
(1)使用CF CG有關(guān)的函數(shù)凉倚,內(nèi)存的釋放還是需要手動(dòng)調(diào)用的,不調(diào)用則會(huì)造成內(nèi)存泄漏
CFUUIDRef和CFStringRef都需要手動(dòng)釋放
CGImageRef類似也需要手動(dòng)釋放
(2) Runtime方法中的class_copyIvarList
,class_copyMethodList
這些方法返回的對(duì)象,也需要手動(dòng)釋放(free)
(3) 如果iOS中使用C/C++編程
比如使用Openssl的RSA接口嫂沉,用到一些內(nèi)存的分配操作稽寒,使用結(jié)束需要釋放
[圖片上傳失敗...(image-ff2626-1515303094173)]
在iOS中,怎么避免內(nèi)存泄漏的產(chǎn)生呢输瓜?除了開(kāi)發(fā)經(jīng)驗(yàn)的積累瓦胎,我們也可以通過(guò)兩個(gè)方法來(lái)發(fā)現(xiàn)內(nèi)存泄漏芬萍,以便及時(shí)修復(fù)尤揣,一個(gè)就是上面講到的靜態(tài)分析(Analyze),比較簡(jiǎn)單柬祠;對(duì)于靜態(tài)沒(méi)有檢測(cè)出來(lái)的內(nèi)存泄露問(wèn)題北戏,需要使用動(dòng)態(tài)的方法,下一節(jié)來(lái)分享漫蛔。
<a name="t25" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>無(wú)效數(shù)據(jù)監(jiān)測(cè)(Dead store)
Unused嗜愈、Never read....這個(gè)比較簡(jiǎn)單,修改即可莽龟。
<a name="t26" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>邏輯錯(cuò)誤監(jiān)測(cè)(Logic error)
[圖片上傳失敗...(image-5b1cb5-1515303094174)]
Tag不等于1蠕嫁、2和3的時(shí)候,就會(huì)出現(xiàn)很問(wèn)題了毯盈。len is a garbage value剃毒。建議在聲明變量時(shí),同時(shí)進(jìn)行初始化搂赋。
<a name="t27" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>其他一些warning
對(duì)完美主義者來(lái)說(shuō)赘阀,warning在Xcode里始終都比較礙眼,直接消除脑奠。
<a name="t28" style="box-sizing: border-box; background-color: transparent; color: rgb(79, 161, 219); text-decoration: none; margin: 0px; padding: 0px; font-weight: 400; outline: 0px; background-position: initial initial; background-repeat: initial initial;"></a>Instruments分析
Analyze分析內(nèi)存泄露不能把所有的內(nèi)存泄露查出來(lái)基公,有的內(nèi)存泄露是在運(yùn)行時(shí),用戶操作時(shí)才產(chǎn)生的宋欺。在 C轰豆、C++混編時(shí)胰伍,對(duì)于C++的內(nèi)存分配,也是需要手動(dòng)釋放酸休,比如iOS中使用Openssl來(lái)進(jìn)行RSA的加解密操作喇辽,其中就有很多的內(nèi)存操作,如果不進(jìn)行手動(dòng)釋放雨席,Analyze是檢查不出來(lái)的菩咨,這個(gè)時(shí)候就需要用到Instruments了。
初始化
釋放的代碼注釋掉
[圖片上傳失敗...(image-c34e8f-1515303094170)]
這個(gè)時(shí)候使用Analyze分析陡厘,一點(diǎn)內(nèi)存泄漏也沒(méi)有抽米,我們使用Instruments來(lái)分析
“Product” -> “Profile”:
按上面操作,build成功后跳出Instruments工具糙置,選擇Leaks工具云茸,這時(shí)候Instruments工具就運(yùn)行起來(lái)了,顯示效果如下:
點(diǎn)擊上面的”紅色的圓圈”谤饭,才會(huì)啟動(dòng)對(duì)應(yīng)的APP标捺,這時(shí)”紅色的圓圈”變成”黑色的正方形”:
這時(shí)用戶就可以在APP上任意操作,查看內(nèi)存使用情況揉抵,選擇”Leak Checks”項(xiàng)亡容,右邊的圖中,如果是”綠色的勾”表示內(nèi)存使用正常冤今,”紅色的叉”則表示有內(nèi)存泄漏闺兢,這時(shí)點(diǎn)擊”黑色的正方形”,讓APP暫停下來(lái)戏罢。
代表malloc后沒(méi)有使用free釋放OutBuf的內(nèi)存龟糕。
修改如下: