轉(zhuǎn)發(fā)自
http://blog.csdn.net/skylin19840101/article/details/51500900
謝謝大佬
質(zhì)量問題不僅僅是商品應(yīng)該注重的,在移動互聯(lián)占據(jù)人們各個生活領(lǐng)域的前提下,產(chǎn)品質(zhì)量更顯重要,以最具人氣和潛力的iOS為例,iOS從系統(tǒng)研發(fā)和客戶端軟件開發(fā)環(huán)節(jié)對質(zhì)量的要求異常高,在注重用戶體驗的同時提升產(chǎn)品質(zhì)量,這也是很多用戶非iOS不用的原因,iOS系統(tǒng)已經(jīng)讓移動互聯(lián)網(wǎng)的品質(zhì)得到升級。那么我們在開發(fā)iOS產(chǎn)品時,如何提高它的質(zhì)量呢?
涉及到質(zhì)量問題,這就是一個很大的話題,包括很多方面,比如代碼書寫的質(zhì)量,開發(fā)流程的規(guī)范,項目管理的到位,測試的最后把關(guān)等各個環(huán)節(jié)。編碼需要規(guī)范,命名需要有意義;接口低耦合、高內(nèi)聚、易擴展,代碼能重用、避免重復(fù)代碼;提交代碼后需要做CodeReview;Release前,自測需要充分,包括單元測試、和其他模塊(服務(wù)器)的聯(lián)調(diào)測試,網(wǎng)絡(luò)性能測試,不同機型、不同系統(tǒng)版本、越獄與否等各個方面的測試;通過冒煙測試后才交于QA測試等等。在這里,我們主要分享從開發(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開發(fā)中常見的異常包括以下幾種:
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)的錯誤,所以平時在寫代碼的時候,需要多加注意,加強對參數(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的對象

(3) 不能插入nil的對象
[圖片上傳失敗...(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一般都會有網(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>未實現(xiàn)的方法
(1) .h文件里修改了函數(shù)名,卻忘了修改.m文件里對應(yīng)的函數(shù)名;或者頭文件里定義了函數(shù),.m文件里沒有實現(xiàn)
(2) 使用第三方庫時,沒有添加”-ObjC” flag
(3) MRC時,大部分情況下是因為對象被提前release了,在你心里不希望他release的情況下,指針還在,對象已經(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) 對象類型使用不當(dāng)
[圖片上傳失敗...(image-af0fee-1515303094173)]
因為imagenS本來是NSDictionary的對象,被當(dāng)做NSString來處理了
類似的,NSDictionary的對象被當(dāng)做NSArrary來處理了
<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)處理錯誤
比如數(shù)組長度count, index的下標(biāo)范圍[0, count -1], 在開發(fā)時,可能index的最大值超過數(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)的值是其他變量賦值
這樣會有很大的不確定性, 可能是一個很大的整數(shù)值


這里的值達(dá)到32位和64位整數(shù)的最大值,肯定是一個不正常的參數(shù),比如:

如果找不到str ,則返回NSNotFound,32位下它就是2147483647,64位下18446744073709551615 ,將它作為參數(shù)傳遞則會導(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ù)組
如果一個數(shù)組剛剛初始化,還是空的,就對它進(jìn)行相關(guān)操作
[圖片上傳失敗...(image-4a5ea9-1515303094176)]
所以,為了避免NSRangeException的發(fā)生,必須對傳入的index參數(shù)進(jìn)行合法性檢查,是否在集合數(shù)據(jù)的個數(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這個異常最容易出現(xiàn)在foreach操作中,在for in循環(huán)中如果修改所遍歷的數(shù)組,無論你是add或remove,都會出錯,比如:

執(zhí)行上面的代碼會出現(xiàn)以下的錯誤:

原因就在這 "for in",它的內(nèi)部遍歷使用了類似 Iterator進(jìn)行迭代遍歷,一旦元素變動,之前的元素全部被失效,所以在foreach的循環(huán)當(dāng)中,最好不要去進(jìn)行元素的修改動作,若需要修改,循環(huán)改為for遍歷,由于內(nèi)部機制不同,不會產(chǎn)生修改后結(jié)果失效的問題。
[圖片上傳失敗...(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)存不足的問題,無法分配足夠的內(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
處理文件時的一些異常,最常見的還是存儲空間不足的問題,比如應(yīng)用頻繁的保存文檔,緩存資料或者處理比較大的數(shù)據(jù):[圖片上傳失敗...(image-ec2fa3-1515303094176)]
所以在文件處理里,需要考慮到手機存儲空間的問題。
<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>二、錯誤處理
在進(jìn)行函數(shù)調(diào)用的時候,如果有NSError的參數(shù)傳遞,最好都處理以下,特別是一些網(wǎng)絡(luò)和文件的處理,比如:
文件操作錯誤
比如在刪除文件時,有可能發(fā)生錯誤,我們需要做一些處理
[圖片上傳失敗...(image-60990c-1515303094172)]
網(wǎng)絡(luò)處理錯誤
在使用NSURLConnection進(jìn)行網(wǎng)絡(luò)請求時,發(fā)生錯誤是難免的,必須做相應(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的運行過程中,難免會有I/O的操作,一般的情況,從技術(shù)的角度來看也不會出什么問題,但是業(yè)務(wù)邏輯上可能就需要特別注意了,比如APP升級版本后,可能保存緩存數(shù)據(jù)的格式發(fā)生變化了,讀取數(shù)據(jù)后的處理邏輯也就相應(yīng)發(fā)生變化,這時就需要考慮兼容舊版本數(shù)據(jù)的處理,否則對升級用戶了說,很可能由于異常發(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ù)
客戶端開發(fā)時,對接收到的服務(wù)器數(shù)據(jù),需要特別小心,因為經(jīng)過網(wǎng)絡(luò)傳輸回來的數(shù)據(jù)都是不可信的,什么問題都可能發(fā)生,比如協(xié)議中該有的字段沒有,是整數(shù)型的字段結(jié)果傳了一個子串,Json格式的內(nèi)容變成xml了…,所以我們在解析服務(wù)器數(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)檢查
在開發(fā)完成后,需要對代碼進(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的時候,內(nèi)存泄露是個大問題,一不小心就會中招
[圖片上傳失敗...(image-699c34-1515303094175)]
注意上面的代碼并不是L63行存在泄漏,我們點擊“Potential leak of an object”前面的箭頭,指示會出現(xiàn)一些變化,如下圖。
alloc一個對象的時候,其內(nèi)存計數(shù)內(nèi)存計數(shù)(retain count)+1


因為content的setter方發(fā)會將object的內(nèi)存計數(shù)+1,如下代碼,content是retain屬性。執(zhí)行完L62代碼后,self.content的內(nèi)存計數(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下,這方面的問題就少很多了,但也還是會出現(xiàn)的,比如:
(1)使用CF CG有關(guān)的函數(shù),內(nèi)存的釋放還是需要手動調(diào)用的,不調(diào)用則會造成內(nèi)存泄漏

CFUUIDRef和CFStringRef都需要手動釋放

CGImageRef類似也需要手動釋放
(2) Runtime方法中的class_copyIvarList,class_copyMethodList這些方法返回的對象,也需要手動釋放(free)
(3) 如果iOS中使用C/C++編程
比如使用Openssl的RSA接口,用到一些內(nèi)存的分配操作,使用結(jié)束需要釋放
[圖片上傳失敗...(image-ff2626-1515303094173)]
在iOS中,怎么避免內(nèi)存泄漏的產(chǎn)生呢?除了開發(fā)經(jīng)驗的積累,我們也可以通過兩個方法來發(fā)現(xiàn)內(nèi)存泄漏,以便及時修復(fù),一個就是上面講到的靜態(tài)分析(Analyze),比較簡單;對于靜態(tài)沒有檢測出來的內(nèi)存泄露問題,需要使用動態(tài)的方法,下一節(jié)來分享。
<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>無效數(shù)據(jù)監(jiān)測(Dead store)
Unused、Never read....這個比較簡單,修改即可。
<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>邏輯錯誤監(jiān)測(Logic error)
[圖片上傳失敗...(image-5b1cb5-1515303094174)]
Tag不等于1、2和3的時候,就會出現(xiàn)很問題了。len is a garbage value。建議在聲明變量時,同時進(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
對完美主義者來說,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)存泄露查出來,有的內(nèi)存泄露是在運行時,用戶操作時才產(chǎn)生的。在 C、C++混編時,對于C++的內(nèi)存分配,也是需要手動釋放,比如iOS中使用Openssl來進(jìn)行RSA的加解密操作,其中就有很多的內(nèi)存操作,如果不進(jìn)行手動釋放,Analyze是檢查不出來的,這個時候就需要用到Instruments了。
初始化

釋放的代碼注釋掉
[圖片上傳失敗...(image-c34e8f-1515303094170)]
這個時候使用Analyze分析,一點內(nèi)存泄漏也沒有,我們使用Instruments來分析
“Product” -> “Profile”:

按上面操作,build成功后跳出Instruments工具,選擇Leaks工具,這時候Instruments工具就運行起來了,顯示效果如下:

點擊上面的”紅色的圓圈”,才會啟動對應(yīng)的APP,這時”紅色的圓圈”變成”黑色的正方形”:

這時用戶就可以在APP上任意操作,查看內(nèi)存使用情況,選擇”Leak Checks”項,右邊的圖中,如果是”綠色的勾”表示內(nèi)存使用正常,”紅色的叉”則表示有內(nèi)存泄漏,這時點擊”黑色的正方形”,讓APP暫停下來。


代表malloc后沒有使用free釋放OutBuf的內(nèi)存。
修改如下:
