[iOS-Objective-C] 编码规范

参考

The official raywenderlich.com Objective-C style guide.
Objective-C编码规范:26个方面解决iOS开发问题
自动格式化工具:XcodeClangFormat
Coding Guidelines for Cocoa

规范

  1. 统一使用US英语。

    符合规则的:

    UIColor *myColor = [UIColor whiteColor];
    

    不符合规则的:

    UIColor *myColour = [UIColor whiteColor];  
    UIColor *yanse = [UIColor whiteColor];
    
  2. #import "xxx.h"的排序:以对应的头文件开始,之后是项目内创建的其他文件,下一部分为所有的 Category 文件,最后是第三方库文件。本项目所创建文件部分的排序为:Controller 层文件,View 层文件,Model 层文件,数据层文件,工具类文件。Category 文件部分的排序为:项目内文件的 Category,Foundation 类的 Category,UIKit 类的 Category,第三方库类的 Category。

  3. 使用#pragma mark -对方法进行分类。

    符合规则的:

    #pragma mark - Override //重写方法
    
    - (instancetype)init {}  
    
    - (void)dealloc {}  
    
    - (void)viewDidLoad {}
    
    - (void)viewWillAppear:(BOOL)animated {}  
    
    - (void)didReceiveMemoryWarning {} 
    
    #pragma mark - Public //公有方法 
    
    - (void)publicMethod {} 
    
    #pragma mark - Responder //响应事件的方法,包括处理通知的方法
    
    - (IBAction)submitData:(id)sender {}  
    
    - (void)handleNotification:(NSNotification *)notification {}
    
    #pragma mark - Delegate //代理实现方法(以相应协议名命名)
    
    #pragma mark - Private //私有方法
    
    - (void)privateMethod {}  
    
    #pragma mark - Setter //Setter方法
    
    - (void)setCustomProperty:(id)value {}  
    
    #pragma mark - Getter //Getter方法,通常实现懒加载  
    
    - (id)customProperty {}
    
  4. 在.h文件中方法都为 Public,若较多则以功能划分,与#pragma mark -间不空行,@property间不空行,但两部分之间空一行。

    符合规则的:

    @property NSString (nonatomic) *name;
    @property NSInteger length;
    
    #pragma mark - Category
    - (instancetype)init {}  
    - (void)dealloc {}  
    - (void)viewDidLoad {}
    - (void)viewWillAppear:(BOOL)animated {}  
    - (void)didReceiveMemoryWarning {}   
    

    不符合规则的:

    @property NSString (nonatomic) *name;
    
    @property NSInteger length;
    
    #pragma mark - Category
    
    - (instancetype)init {}
    
    - (void)dealloc {}  
    
    - (void)viewDidLoad {}
    
    - (void)viewWillAppear:(BOOL)animated {}
      
    - (void)didReceiveMemoryWarning {} 
    
  5. 在.m文件中方法以及#pragma mark -间空一行。

    符合规则的:

    #pragma mark - Override
    
    - (instancetype)init {}  
    
    - (void)dealloc {}  
    
    - (void)viewDidLoad {}
    
    - (void)viewWillAppear:(BOOL)animated {}  
    
    - (void)didReceiveMemoryWarning {}   
    

    不符合规则的:

    #pragma mark - Override
    - (instancetype)init {}  
    - (void)dealloc {}  
    - (void)viewDidLoad {}
    - (void)viewWillAppear:(BOOL)animated {}  
    - (void)didReceiveMemoryWarning {}
    
  6. @property的默认属性值不写,只注明非默认值。

    符合规则的:

    @property (weak, nonatomic) NSString *property1;
    @property (nonatomic) NSString *property2;
    @property NSString *property3;
    

    不符合规则的:

    @property (strong, atomic) NSString *property1;
    
  7. 选择,循环结构语句块与上一条语句之间空一行,若在方法的第一行则不需要空行。

    符合规则的:

    NSInteger a;
    NSInteger b;
    
    if (a > b) {
        NSLog(@"a > b");
    }
    

    不符合规则的:

    NSInteger a;
    NSInteger b;
    if (a > b) {
        NSLog(@"a > b");
    }
    
  8. block 代码块与上一条语句之间空一行,若在方法的第一行则不需要空行。

    符合规则的:

    NSInteger a;
    NSInteger b;
    
    [UIView animateWithDuration:1.0 animations:^{  
        // something  
    }];
    

    不符合规则的:

    NSInteger a;
    NSInteger b;
    [UIView animateWithDuration:1.0 animations:^{  
        // something  
    }];
    
  9. 缩进使用4个空格,可在 Xcode 中的Preferences->Text Editing->Indentation中设置。

  10. 空行中不要留有空字符,可在 Xcode 中的Preferences->Text Editing->Editing中设置。

  11. 方法及其他语句块的大括号总是在同一行语句打开但在新行中关闭。

    符合规则的:

    if (success) {  
        //Do something  
    } else {  
        //Do something else  
    }
    

    不符合规则的:

    if (success)
    {  
        //Do something  
    } else 
    {  
        //Do something else  
    }
    
  12. 选择,循环结构语句块中关键字,条件,大括号之间都有一个空格。

    符合规则的:

    if (success) {  
        //Do something  
    } else {  
        //Do something else  
    }
    

    不符合规则的:

    if(success){  
        //Do something  
    }else{  
        //Do something else  
    }
    
  13. return语句前空一行。

    符合规则的:

    - (NSString *)method {
        NSString *string = @"";
    
        return string;
    }
    

    不符合规则的:

    - (NSString *)method {
        NSString *string = @"";
        return string;
    }
    
  14. 避免以冒号对齐的方式来声明,定义和调用方法。

    符合规则的:

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    }
    
    // blocks are easily readable  
    [UIView animateWithDuration:1.0 animations:^{  
    // something  
    } completion:^(BOOL finished) {  
    // something  
    }];
    

    不符合规则的:

    - (UITableViewCell *)tableView:(UITableView *)tableView
             cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    }
    
    // colon-aligning makes the block indentation hard to read  
    [UIView animateWithDuration:1.0  
                     animations:^{  
                         // something  
                     }  
                     completion:^(BOOL finished) {  
                         // something  
                     }]; 
    
  15. 方法定义时,修饰符与返回类型之间有一个空格,方法名与其后的开始大括号之间有一个空格。在方法各个段之间应该也有一个空格,在参数之前应该包含一个具有描述性的关键字来描述参数。不要使用and作为多个参数的说明。各参数段的说明,首字母也要小写。

    符合规则的:

    - (void)method {
    }
    - (void)setExampleText:(NSString *)text image:(UIImage *)image;
    - (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
    - (id)viewWithTag:(NSInteger)tag;
    - (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
    

    不符合规则的:

    -(void)method{
    }
    -(void)setT:(NSString *)text i:(UIImage *)image;
    - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
    - (id)taggedView:(NSInteger)tag;
    - (instancetype)initWithWidth:(CGFloat)width AndHeight:(CGFloat)height;
    - (instancetype)initWith:(int)width And:(int)height;  // Never do this.
    
  16. 统一使用语法糖。特别注意 nil 值不能传入 NSArray 和
    NSDictionary 字面值,因为这样会导致 crash。

    符合规则的:

    @property (nonatomic) NSString *string;
    
    - (void)text {
        NSString *localString = self.string;
        self.string = @"";
        NSNumber *number = @(1);
        NSDictionary *dictionary = @{@"value1":@"key1"};
        NSString *value1 = dictionary[@"key1"];
        NSArray *array = @[@"value"];
        NSString *value = array[1];
    }
    

    不符合规则的:

    @property (nonatomic) NSString *string;
    
    - (void)text {
        NSString *localString = [self string];
        [self setString:@""];
        NSNumber *number = [NSNumber numberWithInt:1];
        NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"value1",@"key1", nil];
        NSString *value1 = [dictionary objectForKey:@"key1"];
        NSArray *array = [NSArray arrayWithObjects:@"value", nil];
        NSArray *array = @[@"value", nil];
        NSString *value = [array objectAtIndex:1];
    }
    
  17. 注释应该用来解释这段特殊代码为什么要这样做,而不是翻译代码做了什么。任何被使用的注释都必须保持最新或被删除。

  18. 由于Objective-C中没有命名空间的概念,所以一般会在所有类前加一个项目自定义的大写缩写前缀,一般为两到三个字符。尽量使用描述完整的变量名(包括property)和方法名,因为有编辑器的自动提示功能,可以允许命名较长,使用驼峰式命名规则,但首字母要小写,变量命名的一个原则是在变量名的最后指明变量的类型。

    符合规则的:

    UIButton *settingsButton;
    NSArray *viewArray = [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
    

    不符合规则的:

    UIButton *setBut;
    NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
    
  19. 常量应该使用驼峰式命名规则。全局常量以小写的字母k加项目前缀开头,一个类内部使用的静态常量则不需要加项目前缀,之后所有的单词首字母大写。

    符合规则的:

    NSString const* kSRMConstString = @"";
    static NSString const* kSRMStaticConstString = @"";
    

    不符合规则的:

    static NSString const* constString = @"";
    
  20. 局部变量的命名不要以下划线开始,以便和 property 默认生成的成员变量区别。除了 property 的 setter 和 getter 方法外,如果没有特殊情况,不要使用默认生成的变量,应直接操作属性。

  21. 统一使用属性,避免使用成员变量。属性完全可以替代成员变量,当不对 setter 和 getter 方法进行自定义时,属性与成员变量效果一致,使用属性可以灵活的随时加以额外的处理。

    符合规则的:

    @interface SRClass: NSObject  
    @property (nonatomic) NSString *variable;  
    @end
    

    不符合规则的:

    @interface SRClass: NSObject {  
        NSString *variable;  
    }
    
  22. 星号表示声明的变量是指针类型

    符合规则的:

    NSString *string = @"";
    

    不符合规则的:

    NSString * string = @"";
    NSString* string = @"";
    

    特殊情况:

    NSString *const string= @"";
    
  23. NSString、NSDictionary、NSArray 应该使用 copy 属性特性。即使你声明的是以上类型的属性,有人可能传入一个对应的可变版本的实例,然后在你没有注意的情况下修改它

    符合规则的:

    @property (copy, nonatomic) NSString *variable;
    

    不符合规则的:

    @property (nonatomic) NSString *variable;
    
  24. 关于代码段中其他情况的空行,super 的方法可以在方法中的任意位置调用,所以 super 方法调用的代码前后不需要空行,原则上除相应语法代码段的空行外在方法中不出现其他空行,如果两个代码段之间需要空行区分,考虑是否应分成两个新方法。如果真的有需要空行区分,可以加一行简单注释分隔,不空行。

  25. 不使用宏来定义常量,常量是容易重复被使用且无需通过查找和代替就能快速修改值。常量应该使用 const 来声明而不是使用#define。

    符合规则的:

    NSString *const kSRString = @"string";
    static CGFloat const kStaticFloat = 50.0;
    

    不符合规则的:

    #define kSRString @"string"  
    #define kSRStaticFloat 50.0
    
  26. 文件内部使用的字符串常量

    static NSString *const kFoo = @"Foo";
    

    供多个模块使用的公共字符串常量,名称要加项目缩写前缀

    // .m 文件中
    NSString *const Foo = @"Foo";
    // .h 文件中
    extern NSString *const Foo;
    

    常量名以小写字母 k 开头是参照匈牙利命名法(Hungarian Notation),表示常量 constant 的意思。内部常量使用这一规则,以便和变量名进行区分。UIKit 不使用这一规范,直接以缩写前缀开始。所以公共常量直接以缩写前缀开始。

  27. 当定义枚举类型时,使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏 NS_ENUM() 来帮助和鼓励你使用固定的基本类型。

    符合规则的:

    typedef NS_ENUM(NSInteger, SREnumType) {  
        SREnumTypeA,  
        SREnumTypeB,  
        SREnumTypeC  
    }; 
    

    不符合规则的:

    enum SREnumType {  
        SREnumTypeA,  
        SREnumTypeB,  
        SREnumTypeC  
    };
    
  28. 当在 case 语句块中声明变量,则该 case 语句块必须被大括号包围,若一个
    case 语句块中没有声明变量,则不加大括号。当switch枚举所有类型时,'default'是不需要的

    符合规则的:

    switch(SREnumType) {
        case SREnumTypeA:
        case SREnumTypeB: {
              NSString *emptyString = @"";
              // code
        }
              break;
        case SREnumTypeC:
              // code
              break;
    }
    
  29. 私有属性应该在类的实现文件中的类扩展(匿名分类)中声明。

    符合规则的:

    @interface SRViewController ()  
    
    @property (nonatomic) GADBannerView *googleAdView; 
    @property (nonatomic) ADBannerView *iAdView;  
    @property (nonatomic) UIWebView *adXWebView;
    
    @end
    
  30. Objective-C 使用 YES 和 NO。因为 true 和 false 应该只在
    CoreFoundation,C 或 C++ 代码使用。既然 nil 解析成 NO,所以没有必要在条件语句比较。不要拿某样东西直接与 YES 比较,因为YES被定义为1而一个 BOOL
    能被设置为8位。

    符合规则的:

    if (someObject) {
    }  
    
    if (![anotherObject boolValue]) {
    } 
    

    不符合规则的:

    if (someObject == nil) {}  
    if ([anotherObject boolValue] == NO) {}  
    if (isAwesome == YES) {} // Never do this.  
    if (isAwesome == true) {} // Never do this.
    
  31. 条件语句主体必须使用大括号包围,即使只有一行代码

    符合规则的:

    if (!error) {  
        return success;  
    } 
    

    不符合规则的:

    if (!error)  
        return success; 
    //or
    if (!error) return success;
    
  32. 当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。

    符合规则的:

    NSInteger value = 5;  
    result = (value != 0) ? x : y;  
    BOOL isHorizontal = YES;  
    result = isHorizontal ? x : y;
    

    不符合规则的:

    result = a > b ? x = c > d ? c : d : y;
    
  33. Init 方法应该遵循 Apple 生成代码模板的命名规则,返回类型应该使用 instancetype 而不是 id。

    符合规则的:

    - (instancetype)init {  
        self = [super init];  
    
        if (self) {  
            // ...  
        }  
    
        return self;  
    }
    
  34. 当类构造方法被使用时,它应该返回类型是 instancetype 而不是
    id。这样确保编译器正确地推断结果类型。

    符合规则的:

    @interface Airplane  
    + (instancetype)airplaneWithType:(RWTAirplaneType)type;  
    @end
    
  35. 当访问 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 };
    
  36. 当使用条件语句编码时,左手边的代码应该是"golden" 或 "happy"路径。也就是不要嵌套if语句,多个返回语句也是OK。

    符合规则的:

    - (void)someMethod {  
        if (![someOther boolValue]) {  
            return;  
        }  
    
        //Do something important  
    }
    

    不符合规则的:

    - (void)someMethod {  
        if ([someOther boolValue]) {  
            //Do something important  
        }  
    }
    
  37. 当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。

    符合规则的:

    NSError *error;  
    
    if (![self trySomethingWithError:&error]) {  
        // Handle Error  
    } 
    

    不符合规则的:

    NSError *error;  
    [self trySomethingWithError:&error]; 
    
    if (error) {  
        // Handle Error  
    } 
    
  38. 单例对象应该使用线程安全模式来创建共享实例。

    符合规则的:

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

    不符合规则的:

    + (instancetype)sharedInstance {
         static MyClass *shared = nil;
    
         if(shared == nil) {
            shared = [[MyClass alloc] init];
         }
    
         return shared;
    }
    
  39. 物理文件目录应该与 Xcode 工程文件目录保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。

  40. 除了使用带参数的init方法初始化实例时与alloc方法链式调用,其他情况一般不链式调用。

    符合规则的:

    MyClass *myClass = [[MyClass alloc] initWithName:@""];
    NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter];
    [notificationCenter addObserver:self selector:@selector(disposeNotification:) name:SRNotification object:nil];
    

    不符合规则的:

    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disposeNotification:) name:SRNotification object:nil];
    
  41. 当变量不使用默认的 strong 进行修饰时,需明确写出指定的修饰符

    符合规则的:

    MyClass * __weak myWeakReference;
    MyClass * __unsafe_unretained myUnsafeReference;
    

    不符合规则的:

    __weak MyClass *myWeakReference;
    __unsafe_unretained MyClass *myUnsafeReference;
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,519评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,842评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,544评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,742评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,646评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,027评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,513评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,169评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,324评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,268评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,299评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,996评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,591评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,667评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,911评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,288评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,871评论 2 341

推荐阅读更多精彩内容