iOS代碼編程規範 根據項目經驗彙總

帶出幾十位從零開始學iOS的實習生或試用期的開發人員後,以爲真的是千人千面,每一個人寫的代碼都風格迥異,若是沒有一個文檔規範,每次都和新人進行口頭的說教,大概本身是不用敲代碼了,因此吃了虧了就開始編寫iOS的編程規範。因爲本人在寫iOS代碼前一直是C語言的開發,因此不少規範都受C語言的影響。c++

編程規範.png

與你們分享下我總結的編程規範,有不合適的請你們指出(最好能舉例說明爲什麼很差,並給一個好的推薦)^_^編程

1. 指導原則

(1)首先是爲人編寫程序,其次纔是計算機。

軟件的生命週期貫穿產品的開發,測試,生產,用戶使用,版本升級和後期維護等過程,只有易讀,易維護的軟件代碼才具備生命力。數組

(2)保持代碼的簡明清晰,避免過度的編程技巧。

簡單是最美。不要過度追求技巧,不然會下降程序的可讀性。安全

(3)編程時首先達到正確性,其次考慮效率。

編程首先考慮的是知足正確性,健壯性,可維護性,可移植性等質量因素。bash

(4)編寫代碼時須要考慮到代碼的可測試性。

不能夠測試的代碼是沒法保障質量的。實現設計功能的同時,要提供能夠測試、驗證的方法。服務器

(5)函數(方法)是爲一特定功能而編寫,不是萬能工具箱。

方法是一個處理單元,是由特定功能的,因此應該很好地規劃方法,不能是全部東西都放在一個方法裏實現。app

(6)鼓勵多註釋。

當代碼有改動時,註釋必定要修改,而且不要添加多餘或重複的註釋。框架

2. 佈局

程序佈局的目的是現實出程序良好的邏輯結構,提升程序的準確性、連續性、可讀性、可維護性。統一的程序佈局和編程風格,有助於提升整個項目的開發質量,提升開發效率,下降開發成本。異步

遵循統一的佈局順序書寫頭文件和實現文件:ide

文件頭註釋 #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或則一、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

總結寫完了,複製黏貼好累啊。發現不少條在開發中都沒有遵照,由於需求變更實在太頻繁了,而且要求的開發時間實在過短。吐槽下產品組,能不能多考慮多考慮而後聽聽咱們開發的意見啊。!_!

相關文章
相關標籤/搜索