IOS编码规范

一.总体原则

  1. 需求是暂时的,只有变化才是永恒的,面向变化编程,而不是面向需求编程。
  2. 不要过分追求技巧,降低程序的可读性
  3. 简洁的代码可以让bug无处藏身。要写出明显没有bug的代码,而不是没有明显bug的代码
  4. 先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。

二.编码规范

1.if表达式写法

推荐:

if (!error) {
    return success;
}

不推荐:

if (!error)
    return success;

和:

if (!error) return success;
  • 变量和常量比较
    推荐:
[myValue isEqual:@42];

不推荐:

[@42 isEqual:myValue];
  • nil和BOOL值检查
    推荐:
if (someObject) { ...
if (![someObject boolValue]) { ...
if (!someObject) { ...

不推荐:

if (someObject == YES) { ... // Wrong
if (myRawValue == YES) { ... // Never do this.
if ([someObject boolValue] == NO) { ...
  • 避免嵌套if语句,合理使用return可以避免增加代码复杂度,提高代码可读性。

推荐:

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

不推荐:

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

2.三元运算符

三元运算符 ? 应该只用在它能让代码更加清楚的地方。 一个条件语句的所有的变量应该是已经被求值了的。

推荐:

result = a > b ? x : y;

不推荐:

result = a > b ? x = c > d ? c : d : y;

当三元运算符的第二个参数(if 分支)返回和条件语句中已经检查的对象一样的对象的时候,下面的表达方式更灵巧:

推荐:

result = object ? : [self createObject];

不推荐:

result = object ? object : [self createObject];

3.Case语句

除非编译器强制要求,括号在 case 语句里面是不必要的。但是当一个 case 包含了多行语句的时候,需要加上括号。

switch (condition) {
    case 1:
        // ...
        break;
    case 2: {
        // ...
        // 多行语句需要加上括号
        break;
       }
    case 3:
        // ...
        break;
    default:
        // ...
        break;
}

有时候可以使用 fall-through 在不同的 case 里面执行同一段代码。一个 fall-through 是指移除 case 语句的 “break” 然后让下面的 case 继续执行。

switch (condition) {
    case 1:
    case 2:
        // code executed for values 1 and 2
        break;
    default:
        // ...
        break;
}

当在 switch 语句里面使用一个可枚举的变量的时候,default 是不必要的。比如:

switch (menuType) {
    case ZOCEnumNone:
        // ...
        break;
    case ZOCEnumValue1:
        // ...
        break;
    case ZOCEnumValue2:
        // ...
        break;
}

4.函数

  • 单一原则

    一个函数只做一件事,每个函数的职责都应该划分的很明确。

    推荐:

dataConfiguration()
viewConfiguration()

不推荐:

void dataConfiguration()
{   
   ...
   viewConfiguration()
}
  • 需要对参数的正确性和有效性进行检查
  • 对相同功能进行封装
  • 将函数内部比较复杂的逻辑提取出来作为单独的函数

三.命名规范

1.统一要求

推荐使用长的、描述性的方法和变量名。尽可能遵守Apple命名约定

推荐:

UIButton *settingsButton;

不推荐:

UIButton *setBut;

2.类名

大驼峰式命名:每个单词的首字母都采用大写字母

例如:MainViewController

3.私有变量

  • 小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母大写。
  • 私有变量:以下划线开头 例如:NSString *_peopleName

4.属性

  • 小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母大写。
  • 关键字顺序:原子性、读写权限、内存管理
  • Block、NSString属性使用copy修饰

5.常量

  • 常量应该以驼峰法命名,并以相关类名作为前缀。

    推荐:

static const NSTimeInterval VAReportViewControllerFadeOutTime = 0.4;

不推荐:

static const NSTimeInterval fadeOutTime = 0.4;
  • 推荐使用常量来代替字符串字面值和数字,这样能够方便复用,而且可以快速修改而不需要查找和替换。常量应该用 static声明为静态常量,而不要用 #define,除非它明确的作为一个宏来使用。
    推荐:
static const NSTimeInterval VAReportViewControllerFadeOutTime = 0.4;
static NSString * const VAReportViewControllerCellIdentifier = @"VAReportViewControllerCell";

不推荐:

#define VAReportViewControllerFadeOutTime 0.4
#define VAReportViewControllerCellIdentifier @"VAReportViewControllerCell"

对于外部可见的常量,在头文件中以这样的形式暴露给外部:

extern NSString *const VAReportViewControllerCellIdentifier;

并在实现文件中为其赋值。

只有公有的常量才需要添加命名空间作为前缀。尽管实现文件中私有常量的命名可以遵循另外一种模式,你仍旧可以遵循这个规则。

6.枚举

  • 命名规则和类的命名一致。
  • 枚举内容的命名需要以该枚举类型名称开头。
    NS_ENUM 定义通用枚举 NS_OPTIONS 定义位移枚举

例如:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
    UIViewAnimationTransitionNone,
    UIViewAnimationTransitionFlipFromLeft,
    UIViewAnimationTransitionFlipFromRight,
    UIViewAnimationTransitionCurlUp,
    UIViewAnimationTransitionCurlDown,
};
typedef NS_OPTIONS(NSUInteger, UIControlState) {
    UIControlStateNormal       = 0,
    UIControlStateHighlighted  = 1 << 0,
    UIControlStateDisabled     = 1 << 1,
    UIControlStateSelected     = 1 << 2        
};

7.指定初始化方法和间接初始化方法

Objective-C 有指定初始化方法(Designated Initializer)和间接(Secondary Initializer)初始化方法的观念。designated 初始化方法是提供所有的参数,secondary 初始化方法是一个或多个,并且提供一个或者更多的默认参数来调用 designated 初始化的初始化方法。

  • 在你希望提供你自己的初始化函数的时候,需要遵循下列原则:

    1. 定义你的 Designated Initializer,确保调用了直接父类的Designated Initializer。
    2. 重写直接父类的Designated Initializer。调用你的新的Designated Initializer。
    3. 为新的Designated Initializer 进行文档注释。

    正确的例子:

#import <UIKit/UIKit.h>
@interface VAMessageTableViewCell : UITableViewCell
- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier VA_DESIGNATED_INITIALIZER;
- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name identifier:(NSString *)identifier;
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier VA_UNAVAILABLE_INITIALIZER;
- (instancetype)initWithCoder:(NSCoder *)aDecoder VA_UNAVAILABLE_INITIALIZER;
- (instancetype)init VA_UNAVAILABLE_INITIALIZER;
@end
    
#import "VAMessageTableViewCell.h"
@interface VAMessageTableViewCell ()
@property (nonatomic,copy) NSString *name;
@property (nonatomic,strong) NSDate *date;
@end

@implementation VAMessageTableViewCell
- (VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier {
    //调用直接父类的designated initializer
    self = [super initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
    if (self) {
        _name = name;
        _date = date;
    }
    return self;
}

//Secondary Initializer
- (VAMessageTableViewCell *)initWithName:(NSString *)name identifier:(NSString *)identifier {
    return [self initWithName:name date:nil identifier:identifier];
}

//重写直接父类的Designated Initializer
- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    return [self initWithName:nil date:nil identifier:reuseIdentifier];
}

//重写直接父类的Designated Initializer
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    return [self initWithName:nil date:nil identifier:nil];
}
@end

其中VA_DESIGNATED_INITIALIZERVA_UNAVAILABLE_INITIALIZER定义如下:

#define VA_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer))
#define VA_UNAVAILABLE_INITIALIZER __attribute__((unavailable("please use designated initializer")))

相关宏介绍:

__attribute__((objc_designated_initializer)):用来修饰类的designated initializer初始化方法,如果修饰的方法里没有调用父类的 designated initializer,编译器会发出警告。

__attribute__((unavailable)):可以用来修饰变量,方法,类和协议,表明不可用,如果使用,编译器会发出错误。同deprecated,可以添加说明。

8.方法

推荐:

- (__kindof People *)initWithName:(NSString *)name age:(NSInteger)age birthday:(NSDate *)birthday;

不推荐:

-(instancetype)initWithName:(NSString *)name andAge:(NSInteger)age andBirthday:(NSDate *)birthday;

建议所有返回类的实例的类方法和实例方法使用__kindof ,不要使用id或者instancetype,原因如下:

  • id修饰

    1. 用id修饰返回值类型,不会在编译时进行类型判断。
    2. 返回值类型没有确切提示。
  • instancetype修饰

    虽然会自动识别当前对象的类,但是仍然没有类型提示。

9.通知

当你定义你自己的 NSNotification 的时候你应该把你的通知的名字定义为一个字符串常量,就像你暴露给其他类的其他字符串常量一样。你应该在公开的接口文件中将其声明为 extern 的, 并且在对应的实现文件里面定义。

因为你在头文件中暴露了符号,所以你应该按照统一的命名空间前缀法则,用类名前缀作为这个通知名字的前缀。

同时,用一个 Did/Will 这样的动词以及用 "Notifications" 后缀来命名这个通知也是一个好的实践。

// Foo.h
extern NSString * const ZOCFooDidBecomeBarNotification

// Foo.m
NSString * const ZOCFooDidBecomeBarNotification = @"ZOCFooDidBecomeBarNotification";

四.注释规范

优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在干什么,而不需要注释的辅助。

但并不是说一定不能写注释,有以下三种情况比较适合写注释:

  • 公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)
  • 涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。
  • 容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。

除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。

1.import注释

如果有一个以上的import语句,就对这些语句进行分组,每个分组的注释是可选的。

// Frameworks
#import <QuartzCore>;

// Models
#import "NYTUser.h"

// Views
#import "NYTButton.h"
#import "NYTUserView.h"

2.属性注释

使用//注释,在属性之后,用一个空格隔开。

@property (nonatomic,copy) NSString *name; //用户名

3.方法注释

使用Xcode快捷键command+option+/进行注释:

/**
 方法描述

 @param name 参数描述
 @param date 参数描述
 @param identifier 参数描述
 @return 返回值
 */
- (__kindof VAMessageTableViewCell *)initWithName:(NSString *)name date:(NSDate *)date identifier:(NSString *)identifier VA_DESIGNATED_INITIALIZER;

4.代码块注释

单行使用// 多行使用/**/

5.#pragma

  • #pragma mark -是一个在类内部组织代码并且帮助你分组方法实现的好办法。

    分离示范:

#pragma mark - Get

#pragma mark - Set

#pragma mark - Life Cycle
- (void)viewDidLoad {
    [super viewDidLoad];
}
- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
}
- (void)viewDidAppear:(BOOL)animated {
    [super viewDidAppear:animated];
}

#pragma mark - Super Class

#pragma mark - Event Responder

#pragma mark - TableView Delegate DataSource


6.TODO MARK FIXME 标记

    //MARK:标记一下
    //TODO:通知即将要做
    //FIXME:你想要修改的bug

参考资料:
https://github.com/oa414/objc-zen-book-cn/
https://www.jianshu.com/p/21f059f04181
Coding Guidelines for Cocoa

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

推荐阅读更多精彩内容

  • 约定 在我看来,开发规范像是一条可供参考的标准线。不同开发者可以根据这条标准线来规范自己的开发行为,尤其是在大的项...
    xxzsxxzs阅读 615评论 1 0
  • 前言 Swift 贡献给社区 作者 关于中文翻译 条件语句 尤达表达式 nil 和 BOOL 检查 黄金大道 复杂...
    一条鱼的星辰大海阅读 359评论 0 1
  • 禅与 Objective-C 编程艺术 (Zen and the Art of the Objective-C C...
    GrayLand阅读 1,601评论 1 10
  • 本文档旨在总结出一份通用的编码规范,欢迎随时探讨补充。 喜欢我的可以关注收藏我的个人博客:Ro.bber 代码格式...
    Ro_bber阅读 866评论 0 1
  • 摘要 为了规范看准iOS编码而作。主要包含编码格式和命名规范两大章节。 在写之前对下面所用的有些词汇进行一些约定:...
    叫我公爵大人阅读 267评论 0 1