<bdo id="vwktd"></bdo>
<bdo id="vwktd"></bdo><bdo id="vwktd"></bdo>

    1. <menuitem id="vwktd"><dfn id="vwktd"></dfn></menuitem>
    2. <track id="vwktd"><div id="vwktd"></div></track>

      <track id="vwktd"></track>

        iOS代碼編程規范-根據項目經驗匯總

        2016-05-19 23:27:10來源:開發者(KaiFaX)作者:蝴蝶之夢天使

        與大家分享下我總結的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 @"1/quotest"
        #define URL_UPDATE_QUOTE_LIST @"1/quote/update"
        #define URL_LOGIN   @"1/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

        關鍵詞:iOS編程規范
        被老头下药强奷到爽

        <bdo id="vwktd"></bdo>
        <bdo id="vwktd"></bdo><bdo id="vwktd"></bdo>

          1. <menuitem id="vwktd"><dfn id="vwktd"></dfn></menuitem>
          2. <track id="vwktd"><div id="vwktd"></div></track>

            <track id="vwktd"></track>