品質問題不僅僅是商品應該注重的,在移動互聯占據人們各個生活領域的前提下,産品品質更顯重要,以最具人氣和潛力的iOS為例,iOS從系統研發和用戶端軟體開發環節對品質的要求異常高,在注重使用者體驗的同時提升産品品質,這也是很多使用者非iOS不用的原因,iOS系統已經讓移動網際網路的品質得到更新。那麼我們在開發iOS産品時,如何提高它的品質呢? 涉及到品質問題,這就是一個很大的話題,包括很多方面,比如代碼書寫的品質,開發流程的規範,項目管理的到位,測試的最後把關等各個環節。編碼需要規範,命名需要有意義;接口低耦合、高内聚、易擴充,代碼能重用、避免重複代碼;送出代碼後需要做CodeReview;Release前,自測需要充分,包括單元測試、和其他子產品(伺服器)的聯調測試,網絡性能測試,不同機型、不同系統版本、越獄與否等各個方面的測試;通過冒煙測試後才交于QA測試等等。在這裡,我們主要分享從開發人員的角度如何提高産品品質,包含的内容如下:
避免異常:
從上面可以看到,iOS開發中常見的異常包括以下幾種:
NSInvalidArgumentException
NSRangeException
NSGenericException
NSInternalInconsistencyException
NSFileHandleOperationException
NSInvalidArgumentException
非法參數異常(NSInvalidArgumentException)是 Objective - C 代碼最常出現的錯誤,是以平時在寫代碼的時候,需要多加注意,加強對參數的檢查,避免傳入非法參數導緻異常,其中尤以nil參數為甚。
集合資料的參數傳遞
比如NSMutableArray, NSMutableDictionary的資料操作
(1) 不能删除nil的key
(2) NSDictionary不能添加nil的對象
(3) 不能插入nil的對象
(4) 其他一些nil參數
其他一些API的使用
(1) NSDictionary不能删除nil的key
APP一般都會有網絡操作,免不了使用網絡相關接口,比如NSURL的初始化,不能傳入nil的http位址:
未實作的方法
(1) .h檔案裡修改了函數名,卻忘了修改.m檔案裡對應的函數名;或者頭檔案裡定義了函數,.m檔案裡沒有實作
(2) 使用第三方庫時,沒有添加”-ObjC” flag
(3) MRC時,大部分情況下是因為對象被提前release了,在你心裡不希望他release的情況下,指針還在,對象已經不在了。 比如:
(4) 對象類型使用不當
因為imagenS本來是NSDictionary的對象,被當做NSString來處理了
類似的,NSDictionary的對象被當做NSArrary來處理了
NSRangeException
越界異常(NSRangeException)也是比較常出現的異常,有如下幾種類型:
數組最大下标處理錯誤
比如數組長度count, index的下标範圍[0, count -1], 在開發時,可能index的最大值超過數組的範圍;
下标的值是其他變量指派
這樣會有很大的不确定性, 可能是一個很大的整數值
這裡的值達到32位和64位整數的最大值,肯定是一個不正常的參數,比如:
如果找不到str ,則傳回NSNotFound,32位下它就是2147483647,64位下18446744073709551615 ,将它作為參數傳遞則會導緻NSRangeException。
使用空數組
如果一個數組剛剛初始化,還是空的,就對它進行相關操作
是以,為了避免NSRangeException的發生,必須對傳入的index參數進行合法性檢查,是否在集合資料的個數範圍内。
NSGenericException
NSGenericException這個異常最容易出現在foreach操作中,在for in循環中如果修改所周遊的數組,無論你是add或remove,都會出錯,比如:
執行上面的代碼會出現以下的錯誤:
原因就在這 "for in",它的内部周遊使用了類似 Iterator進行疊代周遊,一旦元素變動,之前的元素全部被失效,是以在foreach的循環當中,最好不要去進行元素的修改動作,若需要修改,循環改為for周遊,由于内部機制不同,不會産生修改後結果失效的問題。
NSMallocException
這也是記憶體不足的問題,無法配置設定足夠的記憶體空間
NSFileHandleOperationException
處理檔案時的一些異常,最常見的還是存儲空間不足的問題,比如應用頻繁的儲存文檔,緩存資料或者處理比較大的資料:
是以在檔案處理裡,需要考慮到手機存儲空間的問題。
二、錯誤處理
在進行函數調用的時候,如果有NSError的參數傳遞,最好都處理以下,特别是一些網絡和檔案的處理,比如:
檔案操作錯誤 比如在删除檔案時,有可能發生錯誤,我們需要做一些處理
網絡處理錯誤 在使用NSURLConnection進行網絡請求時,發生錯誤是難免的,必須做相應處理
三、本地緩存的相容處理
在APP的運作過程中,難免會有I/O的操作,一般的情況,從技術的角度來看也不會出什麼問題,但是業務邏輯上可能就需要特别注意了,比如APP更新版本後,可能儲存緩存資料的格式發生變化了,讀取資料後的處理邏輯也就相應發生變化,這時就需要考慮相容舊版本資料的處理,否則對更新使用者了說,很可能由于異常發生崩潰。
四、解析網絡資料
用戶端開發時,對接收到的伺服器資料,需要特别小心,因為經過網絡傳輸回來的資料都是不可信的,什麼問題都可能發生,比如協定中該有的字段沒有,是整數型的字段結果傳了一個子串,Json格式的内容變成xml了…,是以我們在解析伺服器資料時,需要有充分的異常處理機制。
五、代碼靜态檢查
在開發完成後,需要對代碼進行靜态檢查,這樣能發現一些記憶體洩露以及一些warning。
記憶體洩露
MRC
在MRC的時候,記憶體洩露是個大問題,一不小心就會中招
注意上面的代碼并不是L63行存在洩漏,我們點選“Potential leak of an object”前面的箭頭,訓示會出現一些變化,如下圖。
alloc一個對象的時候,其記憶體計數記憶體計數(retain count)+1
因為content的setter方發會将object的記憶體計數+1,如下代碼,content是retain屬性。執行完L62代碼後,self.content的記憶體計數就為 2
建議修改方案:
ARC
在ARC下,這方面的問題就少很多了,但也還是會出現的,比如:
(1)使用CF CG有關的函數,記憶體的釋放還是需要手動調用的,不調用則會造成記憶體洩漏
CFUUIDRef和CFStringRef都需要手動釋放
CGImageRef類似也需要手動釋放
(2) Runtime方法中的
class_copyIvarList
,
class_copyMethodList
這些方法傳回的對象,也需要手動釋放(free)
(3) 如果iOS中使用C/C++程式設計 比如使用Openssl的RSA接口,用到一些記憶體的配置設定操作,使用結束需要釋放
在iOS中,怎麼避免記憶體洩漏的産生呢?除了開發經驗的積累,我們也可以通過兩個方法來發現記憶體洩漏,以便及時修複,一個就是上面講到的靜态分析(Analyze),比較簡單;對于靜态沒有檢測出來的記憶體洩露問題,需要使用動态的方法,下一節來分享。
無效資料監測(Dead store)
Unused、Never read....這個比較簡單,修改即可。
邏輯錯誤監測(Logic error)
Tag不等于1、2和3的時候,就會出現很問題了。len is a garbage value。建議在聲明變量時,同時進行初始化。
其他一些warning
對完美主義者來說,warning在Xcode裡始終都比較礙眼,直接消除。
Instruments分析
Analyze分析記憶體洩露不能把所有的記憶體洩露查出來,有的記憶體洩露是在運作時,使用者操作時才産生的。在 C、C++混編時,對于C++的記憶體配置設定,也是需要手動釋放,比如iOS中使用Openssl來進行RSA的加解密操作,其中就有很多的記憶體操作,如果不進行手動釋放,Analyze是檢查不出來的,這個時候就需要用到Instruments了。
初始化
釋放的代碼注釋掉
這個時候使用Analyze分析,一點記憶體洩漏也沒有,我們使用Instruments來分析
“Product” -> “Profile”:
按上面操作,build成功後跳出Instruments工具,選擇Leaks工具,這時候Instruments工具就運作起來了,顯示效果如下:
點選上面的”紅色的圓圈”,才會啟動對應的APP,這時”紅色的圓圈”變成”黑色的正方形”:
這時使用者就可以在APP上任意操作,檢視記憶體使用情況,選擇”Leak Checks”項,右邊的圖中,如果是”綠色的勾”表示記憶體使用正常,”紅色的叉”則表示有記憶體洩漏,這時點選”黑色的正方形”,讓APP暫停下來。
這時你隻要在”Reponsible Frame”中輕按兩下對應的方法,就會跳轉到具體的代碼,比如輕按兩下”MS_RSA_Public_E”:
代表malloc後沒有使用free釋放OutBuf的記憶體。
修改如下: