美文网首页
iOS编码规范

iOS编码规范

作者: yangzming | 来源:发表于2017-06-21 16:03 被阅读0次

    注释规范

    文件头注释

    文件头注释采用如下格式,该注释由xcode自动生成。如果你对其他人的原始代码作出重大的修改,请把你自己的名字添加到作者里面,并且填写相应的备注。

    //
    //  TestComment.m
    //  TestVcard
    //
    //  Created by zhangyang on 2017/6/20.
    //  Copyright © 2017年 myCompany. All rights reserved.
    //
    

    修改项目名称和公司名称的方法请参考下图。修改用户名请在系统偏好设置->用户账户中进行修改。


    image.png

    接口注释

    每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。使用如下格式:

    /**
     添加描述
     
     @author zhangyang
     @date 2017/06/20
     */
    

    可以将该段注释放在代码段中,如下图,我设置了快捷键为cmc。注意添加完成后需要修改描述和日期。

    变量和常量注释

    下面几种情况下的常量和变量,都要添加注释说明,在上方添加注释。//和内容后面有一个空格。

    • 接口中定义的所有常量
    • 公有类的公有常量
    • 枚举类定义的所有枚举常量
    • 实体类的所有属性变量
    // 用户名
    @property (nonatomic, strong) NSString *userId;
    
    //用户密码
    @property (nonatomic, strong) NSString *userPwd;
    

    方法注释

    xcode 8已经集成了VVDocument,直接使用该注释格式。所有非系统方法都需要添加注释,包括在头文件和实现文件中。在相应的方法上方使用快捷键alt+command+/,即可生成该方法的注释:

    /**
     注册用户
    
     @param userId 用户id
     @param userPwd 用户密码
     @return 0 成功  其他 失败
     */
    - (NSInteger)registerUser:(NSString *)userId userPwd:(NSString *)userPwd;
    

    注意,如果该方法所属的文件是他人创建的,需要添加上用户和日期。如果该方法不是与文件在同一时间创建的,也需要添加时间:

    /**
     注册用户
    
     @author zhangyang
     @date 2017/06/20
     @param userId 用户id
     @param userPwd 用户密码
     @return 0 成功  其他 失败
     */
    - (NSInteger)registerUser:(NSString *)userId userPwd:(NSString *)userPwd;
    

    代码组织分类

    在函数分组和protocol/delegate实现中使用#pragma mark -来分类方法,要遵循以下一般结构:

    #pragma mark - Lifecycle  
    - (instancetype)init {}  
    - (void)dealloc {}  
    - (void)viewDidLoad {}  
    - (void)viewWillAppear:(BOOL)animated {}  
    - (void)didReceiveMemoryWarning {}  
    #pragma mark - Custom Accessors  
    - (void)setCustomProperty:(id)value {}  
    - (id)customProperty {}  
    #pragma mark - IBActions  
    - (IBAction)submitData:(id)sender {}  
    #pragma mark - Public  
    - (void)publicMethod {}  
    #pragma mark - Private  
    - (void)privateMethod {}  
    #pragma mark - Protocol conformance  
    #pragma mark - UITextFieldDelegate  
    #pragma mark - UITableViewDataSource  
    #pragma mark - UITableViewDelegate  
    #pragma mark - NSCopying  
    - (id)copyWithZone:(NSZone *)zone {}  
    #pragma mark - NSObject  
    - (NSString *)description {} 
    

    书写规范

    缩进

    缩进统一为4个空格

    花括号书写

    • 左大括号前不换行
    • 左大括号后换行
    • 右大括号前换行
    • 如果右大括号是一个语句、函数体或类的终止,则右大括号后换行; 否则不换行

    例如,如果右大括号后面是else或逗号,则不换行。

    空格的使用

    if、else、for、switch、while等逻辑关键字与后面的语句留一个空格隔开。

    if (isSuccess) { 
        // TODO while booleanVariable is true
    } else { 
        // TODO else
    }
    

    运算符两边各用一个空格隔开。

    int result = a + b; //Good, = 和 + 两边各用一个空格隔开
    int result=a+b; //Bad,=和+两边没用空格隔开
    for (int i = 0; i < 10; i++);   //good
    for (int i=0;i<10;i++);   //bad
    

    属性定义括号与前后各有一个空格,指针的*号紧靠变量名。如下所示:

    @property (strong, nonatomic) UIWindow *window;
    

    行宽

    尽量让你的代码保持在 80 列之内(或者100)。在xcode中的设置方式如下:


    image.png

    方法声明和定义

    Tip
    - + 和返回类型之间须使用一个空格,参数列表中只有参数之间可以有空格。
    

    方法应该像这样:

    - (void)doSomethingWithString:(NSString *)theString { 
        ...
    }
    

    星号前有空格。当写新的代码时,要与先前代码保持一致。

    如果一行有非常多的参数,将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐。

    - (void)doSomethingWith:(GTMFoo *)theFoo 
                       rect:(NSRect)theRect
                   interval:(float)theInterval { 
        ...
    }
    

    当第一个关键字比其它的短时,保证下一行至少有 4 个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐:

    - (void)short:(GTMFoo *)theFoo 
        longKeyword:(NSRect)theRect 
        evenLongerKeyword:(float)theInterval { 
        ...
    }
    

    方法调用

    Tip
    方法调用应尽量保持与方法声明的格式一致。当格式的风格有多种选择时,新的代码要与已有代码保持一致。
    

    调用时所有参数应该在同一行:

    [myObject doFooWith:arg1 name:arg2 error:arg3];
    

    或者每行一个参数,以冒号对齐:

    [myObject doFooWith:arg1 
                   name:arg2 
                  error:arg3];
    

    不要使用下面的缩进风格:

    [myObject doFooWith:arg1 name:arg2 // some lines with >1 arg 
                  error:arg3];
    
    [myObject doFooWith:arg1 
                   name:arg2 error:arg3];
    
    [myObject doFooWith:arg1 
              name:arg2 // aligning keywords instead of colons 
              error:arg3];
    

    方法定义与方法声明一样,当关键字的长度不足以以冒号对齐时,下一行都要以四个空格进行缩进。

    [myObj short:arg1 
        longKeyword:arg2 
        evenLongerKeyword:arg3];
    

    @public和 @private

    Tip
    @public和 @private访问修饰符应该以一个空格缩进。
    

    与 C++ 中的 public, private以及 protected非常相似。

    @interface MyClass : NSObject { 
     @public
      ... 
     @private 
      ...
    }
    @end
    

    异常

    Tip
    每个 @标签应该有独立的一行,在 @与 {}之间需要有一个空格,@catch
    与被捕捉到的异常对象的声明之间也要有一个空格。
    

    如果决定使用 Objective-C 的异常,那么就按下面的格式。不过最好先看看 避免抛出异常 了解下为什么不要使用异常。

    @try { 
        foo();
    }
    @catch (NSException *ex) { 
        bar(ex);
    }
    @finally {
        baz();
    }
    

    协议名

    Tip
    类型标识符和尖括号内的协议名之间,不能有任何空格。
    

    这条规则适用于类声明、实例变量以及方法声明。例如:

    @interface MyProtocoledClass : NSObject<NSWindowDelegate> { 
        @private id<MyFancyDelegate> delegate_;
    }
    - (void)setDelegate:(id<MyFancyDelegate>)aDelegate;
    @end
    

    块(闭包)

    Tip
    块(block)适合用在 target/selector 模式下创建回调方法时,
    因为它使代码更易读。块中的代码应该缩进 4 个空格。
    

    取决于块的长度,下列都是合理的风格准则:

    • 如果一行可以写完块,则没必要换行。
    • 如果不得不换行,关括号应与块声明的第一个字符对齐。
    • 块内的代码须按 4 空格缩进。
    • 如果块太长,比如超过 20 行,建议把它定义成一个局部变量,然后再使用该变量。
    • 如果块不带参数,^{ 之间无须空格。如果带有参数,^( 之间无须空格,但 ) { 之间须有一个空格。
    // The entire block fits on one line.
    [operation setCompletionBlock:^{ [self onOperationDone]; }];
    
    // The block can be put on a new line, indented four spaces, with the
    // closing brace aligned with the first character of the line on which
    // block was declared.
    [operation setCompletionBlock:^{ 
        [self.delegate newDataAvailable];
    }];
    
    // Using a block with a C API follows the same alignment and spacing
    // rules as with Objective-C.
    dispatch_async(fileIOQueue_, ^{ 
        NSString* path = [self sessionFilePath]; 
        if (path) { 
            // ... 
    }});
    
    // An example where the parameter wraps and the block declaration fits
    // on the same line. Note the spacing of |^(SessionWindow *window) {|// compared to |^{| above.
    [[SessionService sharedService] 
        loadWindowWithCompletionBlock:^(SessionWindow *window) { 
            if (window) { 
                [self windowDidLoad:window]; 
            } else { 
                [self errorLoadingWindow]; 
            } }];
    
    // An example where the parameter wraps and the block declaration does
    // not fit on the same line as the name.
    [[SessionService sharedService] 
        loadWindowWithCompletionBlock:
            ^(SessionWindow *window) { 
                if (window) { 
                    [self windowDidLoad:window]; 
                } else { 
                    [self errorLoadingWindow]; 
                } 
            }];
    
    // Large blocks can be declared out-of-line.
    void (^largeBlock)(void) = ^{ 
        // ...
    };
    [operationQueue_ addOperationWithBlock:largeBlock];
    

    命名规范

    基本原则

    清晰

    既清晰又简洁的命名最好,但以清晰为主,不要用单词简写(非常常用除外,苹果列出的可以接受的简写:链接),尽量使用全称。命名应符合OC标准,让人一看就知道是什么意思,不要让人有疑问,使用英文而不是拼音。

    一致性

    做某件事的代码通常都叫这个名字,比如tag、setStringValue,那你也这么叫。

    驼峰原则

    大驼峰(UserNameLabel):每个单词首字母大写
    小驼峰(userNameLabel):除第一个单词外,其它单词首字母大写

    文件命名

    Tip
    文件名须反映出其实现了什么类 – 包括大小写。
    

    文件的扩展名应该如下:

    后缀 说明
    .h C/C++/Objective-C 的头文件
    .m Ojbective-C 实现文件
    .mm Ojbective-C++ 的实现文件
    .mm Ojbective-C++ 的实现文件
    .cc 纯 C++ 的实现文件
    .c 纯 C 的实现文件
    • 所有自定义的文件名称以项目工程开头命名,eg:“XP”、“ZJG”、“SZ”
    • 针对不同视图控制器,在末尾添加后缀,eg:
      • UIViewController 后缀添加“ViewController”
      • UIView 后缀添加“View”

    类命名

    类名(以及类别、协议名)应首字母大写,并以驼峰格式分割单词

    • 所有类名称以项目工程开头命名,eg:“XP”、“ZJG”、“SZ”
    • 针对不同视图控制器,在末尾添加后缀,eg:
      • UIViewController 后缀添加“ViewController”
      • UIView 后缀添加“View”
      • UIButton 后缀添加“Button”、“Btn”
      • UILabel 后缀添加“Label"

    类别名

    Tip
    类别名应该有两三个字母的前缀以表示类别是项目的一部分
    或者该类别是通用的。类别名应该包含它所扩展的类的名字。
    

    比如我们要基于 NSString 创建一个用于解析的类别,我们将把类别放在一个名为 GTMNSString+Parsing.h 的文件中。类别本身命名为 GTMStringParsingAdditions (是的,我们知道类别名和文件名不一样,但是这个文件中可能存在多个不同的与解析有关类别)。类别中的方法应该以 gtm_myCategoryMethodOnAString: 为前缀以避免命名冲突,因为 Objective-C 只有一个名字空间。

    类名与包含类别名的括号之间,应该以一个空格分隔。

    变量命名

    Tip
    变量名应该以小写字母开头,并使用驼峰格式。
    

    普通变量名

    对于静态的属性(int 或指针),尽量为变量起一个描述性的名字。不要担心浪费列宽,因为让新的代码阅读者立即理解你的代码更重要。例如:

    • 错误的命名:
    int w;
    int nerr;
    int nCompConns;
    tix = [[NSMutableArray alloc] init];
    obj = [someObject object];
    p = [network port];
    
    • 正确的命名:
    int numErrors;
    int numCompletedConnections;
    tickets = [[NSMutableArray alloc] init];
    userInfo = [someObject object];
    port = [network port];
    

    类成员变量

    • 如果只是单纯的private变量,最好声明在implementation里.
    • 如果是类的public属性,就用property写在.h文件里
    • 如果自己内部需要setter和getter来实现一些东西,就在.m文件的类目里用property来声明

    类成员变量(写在implementation里面),以下划线开头,使用单词全名按顺序拼接方式,属性不需要,系统会自动生成下划线开头的成员变量。

    类成员是界面元素的命名

    命名方式为变量含义+视图后缀,采用小驼峰方式书写。

    @property (nonatomic, strong) UIViewController *loginViewController;
    @property (nonatomic, strong) UIView *loginHeaderView;
    @property (nonatomic, strong) UILabel *loginLabel;
    @property (nonatomic, strong) UIButton *loginBtn;
    

    常量

    常量名(如宏定义、枚举、静态局部变量等)应该以小写字母 k 开头,使用驼峰格式分隔单词,如:kInvalidHandle,kWritePerm。

    方法命名

    方法名应遵守小驼峰原则,首字母小写,其他单词首字母大写,每个空格分割的名称以动词开头。执行性的方法应该以动词开头,小写字母开头,返回性的方法应该以返回的内容开头,但之前不要加get。

    - (void)insertModel:(id)model atIndex:(NSUInteger)atIndex;
    - (instancetype)arrayWithArray:(NSArray *)array;
    

    Delegate命名

    类的实例必须为回调方法的参数之一, 如

    -(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section
    

    回调方法的参数只有类自己的情况,方法名要符合实际含义, 如:

    -(NSInteger)numberOfSectionsInTableView:(UITableView*)tableView
    

    以类的名字开头(回调方法存在两个以上参数的情况)以表明此方法是属于哪个类的, 如:

    -(UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
    

    使用did和will通知Delegate已经发生的变化或将要发生的变化, 如:

    -(NSIndexPath*)tableView:(UITableView*)tableView willSelectRowAtIndexPath:(NSIndexPath*)indexPath;
    -(void)tableView:(UITableView*)tableView didSelectRowAtIndexPath:(NSIndexPath*)indexPath;
    

    图片资源文件名

    • 模块+功能命名法(公共使用:common+功能)。公共模块主要包括统一的背景,导航条,标签,公共的按钮背景,公共的默认图等等;私有模块主要根据app的业务功能模块划分,比如用户中心,消息中心等。
    • 单词全拼,或者大家公认无歧义的缩写(如:nav,bg,btn等)

    个人中心模块中我的消息按钮示例:personal_btn_my_message
    公共模块搜索按钮示例:common_icon_search(或者common_search_icon)

    用枚举表示状态、选项、状态码

    项目中,用枚举来表示一系列的状态、选项和状态码。例如 iOS SDK 中表示 UICollectionView 滑动方向的枚举定义如下:

    typedef NS_ENUM(NSInteger, UICollectionViewScrollDirection) {
        UICollectionViewScrollDirectionVertical,
        UICollectionViewScrollDirectionHorizontal
    };
    

    定义的枚举类型名称应以 2~3 个大写字母开头,而这通常与项目设置的类文件前缀相同,跟随其后的命名应采用驼峰命名法则,命名应准确表述枚举表示的意义,枚举中各个值都应以定义的枚举类型开头,其后跟随各个枚举值对应的状态、选项或者状态码。

    对于需要以按位或操作来组合的枚举都应使用 NS_OPTIONS 宏来定义,例如 SDK 中:

    typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
        UIViewAutoresizingNone                 = 0,
        UIViewAutoresizingFlexibleLeftMargin   = 1 << 0,
        UIViewAutoresizingFlexibleWidth        = 1 << 1,
        UIViewAutoresizingFlexibleRightMargin  = 1 << 2,
        UIViewAutoresizingFlexibleTopMargin    = 1 << 3,
        UIViewAutoresizingFlexibleHeight       = 1 << 4,
        UIViewAutoresizingFlexibleBottomMargin = 1 << 5
    };
    

    这样定义的选项能够以 按位或操作符 来进行组合,枚举中每个值均可启用或者禁用某一选项,在使用时,可以使用 按位与操作符 来检测是否启用了某一选项,如下:

    UIViewAutoresizing resizing = UIViewAutoresizingFlexibleWidth | 
    UIViewAutoresizingFlexibleHeight;
    
    if (resizing & UIViewAutoresizingFlexibleWidth) {
        // UIViewAutoresizingFlexibleWidth is set
    }
    

    另外,我们可能使用 switch 语句时,会在最后加上 default 分支,但是若用枚举定义状态机,则最好不要使用 default 分支,因为如果稍后再加了一种状态,那么编译器就会发出警告,提示新加入的状态并未在 switch 分支中处理。假如写上了 default 分支,那么它就会处理这个新状态,从而导致编译器不发出警告,用 NS_ENUM 定义其他枚举类型时也要注意此问题。例如在定义代表 UI 元素样式的枚举时,通常要确保 switch 语句能正确处理所有样式。

    编程实践

    Init方法

    Init方法应该遵循Apple生成代码模板的命名规则,返回类型应该使用instancetype而不是id。

    - (instancetype)init {  
      self = [super init];  
      if (self) {  
          // ...  
      }  
      return self;  
    }  
    

    类构造方法

    当类构造方法被使用时,它应该返回类型是instancetype而不是id。这样确保编译器正确地推断结果类型。

    @interface Airplane  
    + (instancetype)airplaneWithType:(RWTAirplaneType)type;  
    @end  
    

    CGRect函数

    当访问CGRect里的x, y, width, 或 height时,应该使用CGGeometry函数而不是直接通过结构体来访问。
    引用Apple的CGGeometry:

    在这个参考文档中所有的函数,接受CGRect结构体作为输入,在计算它们结果时隐式地标准化这些
    rectangles。因此,你的应用程序应该避免直接访问和修改保存在CGRect数据结构中的数据。相反,
    使用这些函数来操纵rectangles和获取它们的特性。
    

    应该:

    CGRect frame = self.view.frame;  
    CGFloat x = CGRectGetMinX(frame);  
    CGFloat y = CGRectGetMinY(frame);  
    CGFloat width = CGRectGetWidth(frame);  
    CGFloat height = CGRectGetHeight(frame);  
    CGRect frame = CGRectMake(0.0, 0.0, width, height);  
    

    不应该:

    CGRect frame = self.view.frame;  
    CGFloat x = frame.origin.x;  
    CGFloat y = frame.origin.y;  
    CGFloat width = frame.size.width;  
    CGFloat height = frame.size.height;  
    CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size }; 
    

    单例模式

    单例对象应该使用线程安全模式来创建共享实例。

    + (instancetype)sharedInstance {  
      static id sharedInstance = nil;  
      static dispatch_once_t onceToken;  
      dispatch_once(&onceToken, ^{  
          sharedInstance = [[self alloc] init];  
      });  
      return sharedInstance;  
    }  
    

    这会防止possible and sometimes prolific crashes

    Xcode工程

    物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在本地文件系统体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。

    日志规范

    只在debug的时候输出日志, release的时候不输出。减少性能损耗和安全隐患。
    在-Prefix.pch(pch全称是“precompiled header”,也就是预编译头文件,该文件里存放的工程中一些不常被修改的代码,比如常用的框架头文件)文件中添加

    #ifdef DEBUG
    #define DLog(fmt, ...) NSLog((@"%s [Line %d] " fmt), __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__)
    #else
    #define DLog(...)
    #endif
    

    其中DLog在开发中根据项目进行命名,在实际使用日志的时候,都使用DLog,不使用NSLog。并且该自定义日志可以输出具体的类和行数。实际开发中,只需要用 DLog(...) 就可以在输出需要信息的同时, 还输出所在类、 函数(方法)名以及行数。可以用这种方法很快找到输出所在的位置。

    参考:http://zh-google-styleguide.readthedocs.io/en/latest/google-objc-styleguide/contents/
    https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html#//apple_ref/doc/uid/10000146-SW1
    http://www.csdn.net/article/2015-06-01/2824818-objective-c-style-guide/1

    相关文章

      网友评论

          本文标题:iOS编码规范

          本文链接:https://www.haomeiwen.com/subject/eqloqxtx.html