iOS程式碼程式設計規範 根據專案經驗彙總
帶出幾十位從零開始學iOS的實習生或試用期的開發人員後,覺得真的是千人千面,每個人寫的程式碼都風格迥異,如果沒有一個文件規範,每次都和新人進行口頭的說教,大概自己是不用敲程式碼了,所以吃了虧了就開始編寫iOS的程式設計規範。由於本人在寫iOS程式碼前一直是C語言的開發,所以很多規範都受C語言的影響。
與大家分享下我總結的程式設計規範,有不合適的請大家指出(最好能舉例說明為何不好,並給一個好的推薦)^_^
1. 指導原則
(1)首先是為人編寫程式,其次才是計算機。
軟體的生命週期貫穿產品的開發,測試,生產,使用者使用,版本升級和後期維護等過程,只有易讀,易維護的軟體程式碼才具有生命力。
(2)保持程式碼的簡明清晰,避免過分的程式設計技巧。
簡單是最美。不要過分追求技巧,否則會降低程式的可讀性。
(3)程式設計時首先達到正確性,其次考慮效率。
程式設計首先考慮的是滿足正確性,健壯性,可維護性,可移植性等質量因素。
(4)編寫程式碼時需要考慮到程式碼的可測試性。
不可以測試的程式碼是無法保障質量的。實現設計功能的同時,要提供可以測試、驗證的方法。
(5)函式(方法)是為一特定功能而編寫,不是萬能工具箱。
方法是一個處理單元,是由特定功能的,所以應該很好地規劃方法,不能是所有東西都放在一個方法裡實現。
(6)鼓勵多註釋。
當代碼有改動時,註釋一定要修改,並且不要新增多餘或重複的註釋。
2. 佈局
程式佈局的目的是現實出程式良好的邏輯結構,提高程式的準確性、連續性、可讀性、可維護性。統一的程式佈局和程式設計風格,有助於提高整個專案的開發質量,提高開發效率,降低開發成本。
遵循統一的佈局順序書寫標頭檔案和實現檔案:
檔案頭註釋 #import 檔案內部使用的巨集 常量定義 檔案內部使用的資料型別 全域性變數 本地變數 類定義/實現
(1)If、else、else if、for、while、do等語句自佔一行,執行語句不得緊跟其後,不論執行語句有多少都要加{}。
“{”前面新增一個空格,緊跟語句後。方法(函式)時,“{”另起一行並獨 佔一行書寫。
if ( numberA > numberB ) {
return numberA;
}
複製程式碼
(2)定義指標型別的變數,“*****” 應該放在變數前。
NSString *str1 = @"Hello World!";
複製程式碼
(3)原始碼中關係較為緊密的程式碼應儘可能相鄰。
CGFloat fWidth;
CGFloat fLength;
CGFloat fHeight;
複製程式碼
(4)禁止使用TAB鍵,必須使用空格進行縮排,縮排為4個空格。
在Xcode->Preferences->Text Editing->Indentation->Prefer indent using中,將值設定為Spaces。
(5)程式的分界符“{”和“}”在if、else、else if、for、while、do等語句時,“{”前新增空格緊跟語句後。在方法(函式)應獨佔一行並且位於同一列,同時與引用他們的語句對齊。“{}”之內的程式碼塊使用縮排規則對齊。
- (void)dealloc
{
// Do Something
}
if (isUpdated) {
// Do Something
}
複製程式碼
(6)相關的賦值語句等號對齊。
promotionsEntity.promotionImageStr = activityItemDict[@"promotion_image"];
promotionsEntity.promotionIdNum = activityItemDict[@"promotion_id"];
promotionsEntity.promotionNameStr = activityItemDict[@"promotion_name"];
promotionsEntity.promotionColorStr = activityItemDict[@"promotion_color"];
複製程式碼
(7)在switch語句中,每一個case分支和default要用“{}”括起來,“{}”中的內容需要縮排。
(8)函式(方法)塊之間使用一個空行分隔。
(9)一元操作符如!、~、++、--、*、&、和[]、.、->、前後不加空格。
!bValue
~iValue
++iCount
*strSource
&fSum
複製程式碼
(10)多元運算子和他們的運算元之間至少需要一個空格。
fWidth = 5 + 5;
fLength = fWidth * 2;
fHeight = fWidth + fLength;
複製程式碼
(11)關鍵字之後要留空格。“(”後和“)”前 不新增空格。
if、for、while等關鍵字之後應留一個空格再跟左括號”(”。
if (0 == fWidth)
複製程式碼
(12)方法名與形參不能留空格,返回型別與方法識別符號有一個空格。
方法名後緊跟”:”,然後緊跟形參,返回型別”(”與”-”之間有一個空格。
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
複製程式碼
(13)註釋符與註釋內容之間要用一個空格進行分割。
/* 設定Frame的值 */
// TO DO
複製程式碼
(14)長表示式(超過80列)要在低優先順序操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當地縮排,使排版整齊。
if ( 0 == fWidth
|| 0 < fLength )
複製程式碼
(15)函式(方法)宣告時,型別與名稱不允許分行書寫。
- (void)didReceiveMemoryWarning
複製程式碼
(16)類中功能模組以#pragma mark – 分割,上空兩行,下空一行
#pragma mark – UITextFieldDelegate
複製程式碼
(17)方法實現時,引數過長則每個引數用一行,以冒號對齊。
- (void)doSomethingWith:(NSString *)theFoo
rect:(CGRect)theRect
interval:(CGFloat)theInterval
{
複製程式碼
如果方法名比引數名短,則每個引數佔用一行,至少縮排4個字元,且為垂直對齊(非冒號對齊)。
- (void)short:(NSString *)theFoot
longKeyword:(CGRect)theRect
evenLongerkeyword:(float)theInterval
{
}
複製程式碼
(18)方法呼叫沿用宣告方法的習慣。
在同一行
[self doSomethingWith:@"test" rect:self.view.frame interval:1.0f];
複製程式碼
或 冒號對齊
[self doSomethingWith:@"test"
rect:self.view.frame
interval:1.0f];
複製程式碼
(19)@public 和 @private 使用單獨一行,且縮排1個字元
(20)Protocals宣告中型別識別符號、代理名稱、尖括號間不留空格。
id<MyProtocalDelegate> delegate;
複製程式碼
在類宣告中包含多個protocal,每個protocal佔用一行,縮排2個字元。
@interface CustomBackButtonViewController () <UITextFieldDelegate,
MyProtocalDelegate,
UITabBarControllerDelegate,
UITabBarDelegate>
複製程式碼
也可以與第一個對齊,保持清晰易讀
@interface ShopViewController () <UIGestureRecognizerDelegate,
HXSClickEventDelegate,
UITableViewDelegate,
UITableViewDataSource>
複製程式碼
如果並非所有方法都是必須得,使用@optional標示。
(21)UIViewController.m的檔案佈局,不同變數和方法的先後順序:
<1>#import “LocationCustomButton.h”
最開始是匯入需要使用到的其他class標頭檔案,標頭檔案按型別分類
// controller
#import “TextSelectionViewController.h"
// model
#import "PayPasswordUpdateModel.h"
#import "WalletModel.h"
// views
#import "PayPasswordAlertView.h"
複製程式碼
<2>#define KEY_BANK_TAIL @“bank_tail"
新增巨集定義,將在檔案中需要使用到的常量,字串等用巨集定義。
<3>@property (nonatomic, strong) NSArray *bankCardList;
property的屬性定義。不用將需要使用的methods宣告,在iOS7後Methods已經不需要先宣告再使用了。
<4>- (void)viewDidLoad
#pragma mark - Life Cycle
新增生命週期的方法
- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
-(void)viewDidDisappear:(BOOL)animated
- (void)didReceiveMemoryWarning
- (void)dealloc
複製程式碼
<5>#pragma mark - override
過載父類的方法
<6>#pragma mark - Intial Methods
初始化的方法,如
- (void)initialNavigationBar
- (void)initialTableView
複製程式碼
<7>#pragma mark - Target Methods
點選事件或通知事件
<8>#pragma mark - UITextFieldDelegate
Delegate的事件,將所有的delegate放在同一個pragma下
<9>#pragma mark - Setter Getter Methods
所有的property使用懶載入,並將setter或getter放在底部,不影響業務程式碼的閱讀。
<10>#pragma mark - private method
私有方法的程式碼儘量抽取建立公共class。
3. 註釋
(1)多行註釋採用”/* … */”, 單行註釋採用 “// …”.
(2)一般情況下,源程式有效註釋量必須在30%以上。
註釋語言不宜太多也不能太少,註釋語言必須準確、易懂、簡潔。
(3)註釋應與其描述的程式碼相近,對程式碼的註釋應放在其上方或右方相鄰位置,不可放在下方,如放於上方則需要與其上面的程式碼用空行隔開。
(4)註釋與所描述內容進行同樣地縮排。
(5)檔案最前面的註釋,是保留了工程自動生成的註釋,但是需要進行如下修改:檔案作者改為個人的名字,公司名為Insigma Hengtian software Ltd. 如:
//
// ViewController.m
// test
//
// Created by ArthurWang on 14-5-7.
// Copyright (c) 2014年 Insigma HengTian Software Ltd. All rights reserved.
//
複製程式碼
(6)類、協議、結構體註釋。如:
/*
@class HTServerDatamanager
@abstract 非同步連線伺服器管理類
@discussion 非同步請求伺服器,接收到響應後,通過回撥把資料回傳到物件
*/
複製程式碼
(7)成員方法、介面註釋。如:
/*
@method initWithTarget:selector:
@abstract 類初始化函式
@discussion 本類使用時必須呼叫此函式進行初始化
@param target 響應回撥物件
@param selector 回撥物件的selector
@result 類物件
*/
複製程式碼
4. 命名
(1)識別符號要採用英文單詞或其組合,便於記憶和閱讀,切忌使用漢語拼音來命名。
識別符號應當直觀且可以拼讀,可望文知意,英文單詞一般不要太複雜,用詞應當準確。
(2)嚴格禁止使用連續的下劃線,下劃線也不能出現在識別符號頭或結尾。(例項變數及特殊用法除外)
CGFloat variable__name;
NSString *variale___name;
複製程式碼
(3)程式中不要出現僅靠大小區分的相似的識別符號。
NSString *contentOfView;
NSString *ContentOfView;
複製程式碼
(4)巨集、常量名都要使用大寫字母,用下劃線‘_’分割單詞。
#define URL_GAIN_QUOTE_LIST @"/v1/quote/list"
#define URL_UPDATE_QUOTE_LIST @"/v1/quote/update"
#define URL_LOGIN @"/v1/user/login”
複製程式碼
(5)程式中區域性變數不要與全域性變數重名。
儘管區域性變數和全域性變數的作用域不同而不會發生語法錯誤,但容易使人誤解。
(6)使用一致的字首來區分變數的作用域。
g_ 全域性變數 s_ 模組內靜態變數
(7)方法名用小寫字母開頭的單詞組合而成。
方法名力求清晰、明瞭、通過方法名就能夠判斷方法的主要功能。方法名中不同意義欄位之間不要用下劃線連線,而要把每個欄位的首字母大寫以示區分。
- (NSString *)descriptionWithLocale:(id)locale;
複製程式碼
(8)儘量避免名字中出現數字編號,如Value1, Vlaue2等,除非邏輯上的確需要編號。
(9)宣告例項變數,都採用property。
不在類宣告和實現的“{”與“}”之間宣告。
@interface LumberjackViewController ()
{
NSArray *dataSource; // 錯誤:
}
複製程式碼
(10)類名(及其category 和protocal)的首字母大寫,使用字母大寫的形式分割單詞。
5. 變數
變數、常量和資料型別是程式編寫的基礎,是直接關係到程式設計的成敗。
(1)一個變數有且只有一個功能,儘量不要把一個變數用作多種用途。
一個變數只用來表示一個特定功能,不要把一個變數作多種用途。
(2)迴圈語句與判斷語句中,不允許對其它變數進行計算與賦值。
錯誤: if ( 100 > (fWidth = 50 * fLength) )
(3)巨集定義中如果包含表示式或變數,表示式和變數必須用小括號括起來。
#define MY_MIN(A, B) ((A)>(B)?(B):(A))
複製程式碼
(4)巨集名大寫字母
(5)對於全域性變數通過統一的函式訪問。
(6)最好不要在語句塊內宣告區域性變數。
(7)系統常用類作例項變數宣告時加入字尾:
UIViewController: VC UIImage: Img
UIImageView:ImagView UIView:View
UILabel: Lbl UIButton:Btn
UINavigationBar:Nbar UIToolbar:Tbar
UISearchBar:Sbar UITextField:TextField
UITextView:TextView NSArray:Array
NSMutableArray:Marray NSDictionary:Dict
NSMutableDictionary:Mdict NSString:Str
NSMutableString:MStr NSSet:Set
NSMutableSet:Mset
複製程式碼
(8)屬性宣告嚴把許可權,對不需要外部修改的屬性使用readonly。
(9)NSString使用copy而非retain。
在ARC中NSString的使用Strong與Copy的效果一樣。
(10)CFType 使用@dynamic,禁止使用@synthesize
(11)除非必須,使用nonatomic。
(12)定義NSArray和NSDictionary使用泛型,提高程式碼可讀性和健壯性。
NSArray<NSString *> *testArr = [NSArray arrayWithObjects:@"Hello", @"world", nil];
NSDictionary<NSString *, NSNumber *> *dic = @{@"key":@(1), @"age":@(10)};
複製程式碼
在* 符號前面都新增一空格。
6. 表示式
表示式是語句的一部分,他們是不可分割的。
(1)一條語句只完成一個功能。
複雜的語句閱讀起來,難於理解,並容易隱含錯誤。
(2)在表示式中使用括號,使表示式的運算順序更清晰。
由於將運算子的優先順序與結合律熟記是比較困難的,為了防止產生歧義並提高可讀性,即使不加括號時運算順序不會改變,也應當用括號確定表示式的操作順序。
if ( (( 0 == iYear%4 ) && ( 0 != iYear%100 )) || ( 0 == iYear%400 ) )
複製程式碼
(3)避免表示式中的附加功能,不要編寫太複雜的複合表示式。
錯誤:int iResult = iYear++-++iMonth+iDay++;
(4)不可將布林變數和邏輯表示式直接與YES、NO或則1、0進行比較。
if (isSuccess) //真
if (!isSuccess) //假
複製程式碼
(5)在條件判斷語句中,當整型變數與0比較時,不可模仿布林變數的風格,應當將整型變數用“==”或“!=”直接與0比較。
if (0 == iYear)
if ( 0 != iMonth )
複製程式碼
(6)應當將指標變數用“==”或“!=”與nil比較。
指標變數的零值是“空”(即nil),nil的值與0相同,但是兩者含義不同。
if ( nil == strName )
複製程式碼
(7)在switch語句中,每一個case分支必須使用break結尾,最後一個分支必須是default分支。
避免漏掉break語句造成程式錯誤,同時保持程式簡潔。對於多個分支相同處理的情況可以共用一個break,但是要用註釋加以說明。
(8)不可在for迴圈內修改迴圈變數,防止for迴圈失去控制。
(9)迴圈巢狀次數不大於3次。
(10)do while 語句和while語句僅使用一個條件。
(11)如果迴圈體記憶體在邏輯判斷,並且迴圈次數很大,宜將邏輯判斷移到迴圈體的外面。
(12)for語句的迴圈控制變數的取值採用“半開半閉區間”寫法。
這樣做更能適應陣列的特點,陣列的下標屬於一個“半開半閉區間”。
int iMax[1000];
for (int i = 0; i < 1000; i++)
{
NSLog(@"%d", iMax[i]);
}
複製程式碼
(13)將int值轉換為BOOL時應特別小心。
(14)OC中,BOOL被定義為unsigned char,這意味著除了YES(1)和NO(0)外它還可以是其他值。禁止將int直接轉換為BOOL。
(15)將整型值轉換為BOOL的方法:使用三元運算子返回YES/NO,或使用&&,||。
(16)BOOL、_BOOL和bool之間的轉換是安全的,但是BOOL和Boolean間的轉換不是安全的,所以將Boolean堪稱整型值。
(17)在OC中,只允許使用BOOL。
7. 函式
(1)方法不能為多個目的服務。
一個方法一個功能。
(2)在介面中應該儘量少使用外部定義的型別(減少耦合)。
(3)避免函式有太多的引數,引數個數儘量控制在5個以內。
如果引數的確比較多,不妨把這些引數定義成一個結構(或一個類)。
(4)對於有返回值的函式(方法),每一個分支都必須有返回值。
為了保證對被呼叫函式返回值的判斷,有返回值的函式中都每一個退出點都需要有返回值。
(5)對輸入引數的正確性和有效性進行檢查。
很多程式錯誤和崩潰是由非法引數引起的。
(6)防止將函式(方法)的引數作為工作變數。
將函式的引數作為工作變數,有可能錯誤地改變引數內容。對必須改變的引數,最好先用區域性變數代之,最後再將該區域性變數的內容賦給該引數。
(7)函式(方法)體的規模不能太大,儘量控制在200行之內。
冗長的函式不利於除錯,可讀性差。
(8)禁止直接呼叫NSObject的類方法+new,也不要在子類中過載它。使用alloc和init方法。
(9)建立物件時儘量使用autorelease,建立臨時物件時,儘量同時在同一行中autorelease掉,而非使用單獨的release語句。
(10)Dealloc的順序要與變數宣告的順序相同。
這樣有利於review程式碼。 如果dealloc中呼叫其他方法來release變數,將被release的變數以註釋的形式標註清楚。 先release自身成員變數,再呼叫父類dealloc方法。
8. 標頭檔案
(1)申明成員類,應該引用該類申明,而不是包含該類的標頭檔案。
@class MyViewController;
@interface ViewController : UIViewController
{
MyViewController *_myViewController;
}
複製程式碼
(2)共同的介面、結構體、常量和資料型別要定義在同一個標頭檔案裡。
(3)使用#import引入OC和OC++標頭檔案,使用#include引入c和c++標頭檔案。
9. 可靠性
為保證程式碼的可靠性,程式設計時請遵循如下基本原則,優先順序遞減: 正確性 穩定性 可測試性 規範/可讀性 全域性效率 區域性效率 個人表達方式/個人方便性
(1)防止記憶體操作越界
記憶體操作主要是指對陣列、指標、記憶體地址等得操作,記憶體操作越界是軟體系統主要錯誤之一,後果往往非常嚴重,引起崩潰。
(2)當變數釋放後,需要將變數置為nil。
避免因為野指標引起的程式崩潰。
(3)變數在使用前應初始化,防止未初始化的變數被引用。
引用未初始化的變數,會引起程式的崩潰。
(4)指標型別變數必須初始化為nil。
(5)指標不要進行復雜的邏輯或算術操作。
通過複雜的邏輯或算術操作後,指標的位置就很難確定。
(6)減少指標和資料型別的強制型別轉化。
強制型別轉化如果型別強轉錯誤會引起崩潰。
(7)對變數進行賦值時,必須對其值進行合法性檢查,防止越界等現象發生。
(8)非初始化方法中的alloc操作之前必須要nil判斷。
(9)在編寫派生類的賦值時,主要不要忘記對基類的成員變數重新賦值。
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
複製程式碼
(10)私有方法應該在實現檔案中申明。
@interface ViewController ()
- (void)login;
@end
複製程式碼
10. 斷言
斷言是對某種假設條件進行檢查,可以快速發現並定位軟體檔案,同時對系統錯誤進行自動報警。
(1)整個軟體系統應該採用統一的斷言。
assert(str);
(2)正式軟體產品中應把斷言及其它的調測程式碼去掉。
加快軟體執行速度。
11. 其他
(1) 避免過多直接使用立即數。
應該都使用巨集定義,採用立即數不容易理解含義並容易出錯。
(2) 列舉第一個成員要賦初始值。
(3) addObject之前要非空判斷。
(4) release版本程式碼去掉NSLog列印,除了保留異常分支的NSLog。
(5) 禁止在程式碼中直接寫死字串資源,必須要用字串ID替代。
應該考慮多語言國際化,儘量使用NSLocalizedStringFromTable實現對字串ID的引用。
(6) 對於框架設計,邏輯層儘量與UI層分離,降低耦合度。
(7) delegate物件使用assign,禁止使用retain。
因為retain會引起導致迴圈索引導致記憶體洩露,並且對型別的記憶體洩露無法被Instrument發現,極難除錯。
(8) Controller獨立於View和Controller。
不要在View相關的類中新增過多的業務邏輯程式碼,這讓程式碼的可重用性很差。Controller負責業務邏輯程式碼,且Controller的程式碼與View儘量無關。
(9)init方法和dealloc方法是最常用的方法,所以將他們放在類實現的開始位置。
(10) 使用空格將相同的變數、屬性對齊,使用換行分組。
// END
總結寫完了,複製黏貼好累啊。發現很多條在開發中都沒有遵守,因為需求變動實在太頻繁了,並且要求的開發時間實在太短。吐槽下產品組,能不能多考慮多考慮然後聽聽我們開發的意見啊。!_!