Objective-C 编码规范

1.命名git

基本原则

  • 仿照 Cocoa 风格来,使用长命名风格
  • 变量命名推荐的命名语素顺序是:最开头是命名空间简写,而后越重要、区别度越大的语素越要往前放。经典的结构是:做用范围+限定修饰+类型。例:
1
2
3
4
5
6
extern ushort APIDefaultPageSize; // 还行,能明白意思了
extern ushort APIDefaultFetchPageSize; // 加上些限定更好一些
extern ushort APIFetchPageSizeDefault; // 再好些,把重要的往前放

YHToolbarComment // 不推荐
YHCommentToolbar // OK,把类型(toolbar)置后

命名空间

  • 类名、protocols、C 函数、常量、结构体和枚举应带有命名空间前缀;
  • 类方法不要带前缀,结构体字段也不要带前缀

视图命名

为了举例,咱们假定有 UserTagCategory 这几种 model 类型。github

对象展现通常分列表和单个详情,其 view controller 分别使用 ModelListController 和 ModelDetailController,推荐的语素顺序是:Model名 + 限定与修饰 + ListController|DetailController。举例说明:json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// OK
TagUserUsedListController
TagInCategoryListController
CategoryDetailController

// 不推荐,列表统一使用 ListController,不指明是 table view 仍是 collection view
UserFollowerTableViewController

// OK
UserFollowerListController

// 不推荐
UserLikedTagListController

// OK,把显示的对象放在第一位
TagUserLikedListController

// 糟糕,若是是 view controller,必须以 Controller 或 Displayer 结尾
TagListView

常常为了便于多个界面复用,咱们会把 model 的显示统一在一个 view controller 中,在其余界面嵌入这个 view controller。咱们把这类专门管理显示的 view controller 叫作 displayer。如:多线程

1
2
UserListDisplayer
TagListDisplayer

UIView 级别的组件不要以 Controller 或 Displayer 结尾,若是起到管理做用可使用 control 结尾。函数

动机工具

把 model 名放在首位(如 TagUserLikedListController 而不是 UserLikedTagListController)的主要考量是便于搜索。由于 Xcode 不支持乱序搜索,关键词只能从前日后才会有结果。性能

若是限定词在前,由于不一样人理解差别,本身也会遗忘,这个限定词常常是输入不能的,只能搜 TagList 再从列表中查找,等于第一位的查找语素就废掉了。当 model 类型在第一位时,基本上熟悉这个项目的人都清楚要查找的视图显示的是什么类型,第一位正确了,后面添加/修改限定就很方便了。ui

另外一个便利的场景是参考以前界面实现另外一个界面时,查找的大都是相同类型的界面,如实现 UserFollowerListController 参考 UserFollowingListController;而相同限定的场景比较少见,像 UserLikedTagListController 参考 UserLikedCategoryListController 的可能性就较少。编码

PS: 务必常用 Xcode 的 Open Quickly(默认快捷键 Command+Shift+O)spa

方法名

  • 以 alloccopyinitmutableCopynew 开头的方法要注意,它们会改变ARC的行为。[^1]
  • 以 getset 开头的方法有特殊的意义,不要随意定义。
    1. set 是属性默认的设置方法,若是函数不是为了设置类成员,则不要用 set 开头,可用 setup 替代。
    2. get 和属性方法无关,但在 Cocoa 中,其标准行为是经过引用传值,而不是直接返回结果的。欲获取变量,直接以变量名为名,如:userInfomation,而不是 getUserInfomation

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Objective-C
// OK
- (NSString *)name;

// 糟糕,应用上面的写法
- (NSString *)getName;

// OK,但极少使用
- (void)getName:(NSString **)buffer range:(NSRange)inRange;


// OK
- (NSSize)cellSize;

// 糟糕,应用上面的写法
- (NSSize)calcCellSize;


// 对 controller 作通常设置,OK
- (void)setupController;

// 列出具体设置的内容,更好
- (void)setupControllerObservers;

// 糟糕,set 专用于设置属性
- (void)setController;
1
2
3
4
5
6
7
Objective-C
// 来自官方文档
insertObject:atIndex: // OK
insert:at: // 不清晰,插入了什么?at 具体指哪里?
removeObjectAtIndex: // OK
removeObject: // OK
remove: // 糟糕,什么被移除了?

协议名

好的协议名应能马上让人分辨出这不是一个类名,除了以经常使用的 delegate、dateSource 作结尾外,还可使用 …ing 这种形式,如:NSCodingNSCopyingNSLocking

通知命名

基本命名格式是:[与通知相关的类名] + [Did | Will] + [UniquePartOfName] + Notification,例:

1
2
3
4
5
Objective-C
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

临时变量命名

  • 临时变量能够写得很短,如 i、k、vc 这样;
  • 临时变量可使用匈牙利前缀,但数据类型不能够做为前缀:
1
2
3
4
5
// OK
wCell, vcMaster, vToolbar

// 糟糕,数据类型做为前缀
bool_switchState, floatBoxHeight

推荐的前缀:

前缀 含义
ix 序号,起始为0
in 序号(天然数范围),起始为1
if 类型为浮点的“序号”
x 坐标
y 坐标
w 宽度
h 高度
vc 视图控制器
v 视图

常量命名

  • 尽可能避免使用预处理命令#define来定义常量,由于#define定义的 常量是全局的,会将工程中全部定义使用的标示符替换为定义的常量,可能由于定义的标示符重复而引发问题,且不易排查。 
  • 尽可能使用static const来进行常量的定义,好处是能够明显地知道常量的类型,且避免用预处理命令#define引发的问题 
  • 用static const进行常量的定义的时候,首写字母要大写,并加小写字母前缀k
  • 对于枚举常量,使用typedef NS_ENUM,对于位掩码常量,使用typedef NS_OPTION,这两个宏已经对C和C++进行了不一样的枚举处理 

除以上规则约定外,其余常量约定了如下前缀:

前缀 含义
k 宏常量
CDEN Core Data entity name
UDk User Default key
KCk Key Chain key
APIURL 接口地址

另见:常量管理

资源命名

图片资源在放入xcassets中相应视图控制器文件夹的基础上,根据[相应的功能] + [Btn | BtnClick | Icon],例:

1
2
3
UserAvatarDefaultIcon
PostCommentBtn
ClickShowWebsiteBtn

大小写

  • 类名采用大驼峰(UpperCamelCase
  • 类成员、方法小驼峰(lowerCamelCase
  • 局部变量大小写首选小驼峰,也可以使用小写下划线的形式(snake_case
  • C函数的命名用大驼峰

缩写

可使用普遍使用的缩写,如 URLJSON,而且缩写要大写。但像将download简写为dl这种是不能够的。

1
2
3
4
5
6
7
8
9
10
11
Objective-C
// OK
ID, URL, JSON, WWW

// 糟糕
id, Url, json, www

destinationSelection // OK
destSel // 糟糕
setBackgroundColor: // OK
setBkgdColor: // 糟糕

其余

i,j专用于循环标号

为私有方法命名不要直接以“”开头,而应以“命名空间”开头。

代码格式化

空格

类方法声明在方法类型与返回类型之间要有空格。

1
2
3
4
5
6
Objective-C
// 糟糕
-(void)methodName:(NSString *)string;

// OK
- (void)methodName:(NSString *)string;

条件判断的括号内侧不该有空格。

1
2
3
4
5
6
7
8
9
// 糟糕
if ( a < b ) {
// something
}

// OK
if (a < b) {
// something
}

关系运算符(如 >=!=)和逻辑运算符(如 &&||)两边要有空格。

1
2
3
4
5
// OK
(someValue > 100)? YES : NO

// OK
(items)?: @[]

二元算数运算符两侧是否加空格不肯定,根据状况本身定。一元运算符与操做数以前没有空格。

多个参数逗号后留一个空格(这也符合正常的西文语法)。

花括号

方法的花括号推荐另起一行。方法内部须要写在一行。

1
2
3
4
5
6
7
8
9
- (void)methodName:(NSString *)string {
↑空格 ↑空格,推荐花括号在一行
if () {
空格↑ ↑空格,花括号不要另起一行
}
else {
要换行↑ ↑空格,花括号不要另起一行
}
}

动机

Xcode 默认的花括号位置是这样的:方法内部的各类补全都是在同一行的;方法定义的比较混乱,默认模版另起一行,但从 Interface Builder 中连线生成的方法在同一行的。

考虑到 Xcode 的默认行为,方法内部要另起一行,方法所在行不强制定死。另外,模版能够定制,而 IB 生成的代码不可定制,因此不另起一行的写法优先。

另起一行的写法在代码折叠后很是难看。

空行

相对独立的程序块之间、变量说明以后必须加空行。

折行

与多数其余规范不一样,不建议手动折行。

动机

手动折行的效果严重宽度依赖于窗口宽度——窗口过宽浪费宝贵的屏幕空间,较窄时可能没法阅读。并且 Xcode 自动折行的效果仍是不错的。

代码组织

  • 函数长度(行数)不该超过100行。例外:对于顺序执行的初始化函数,若是其中的过程没有提取为独立方法的必要,则没必要限制长度。
  • 单个文件方法数不该超过30个
  • 不要按类别排序(如把IBAction放在一块),应按任务把相关的组合在一块儿
  • 禁止出现超过两层循环的代码,用函数或block替代。

尽早返回错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
// 为了简化示例,没有错误处理,并使用了伪代码

// 糟糕的例子
- (Task *)creatTaskWithPath:(NSString *)path {
Task *aTask;
if ([path isURL]) {
if ([fileManager isWritableFileAtPath:path]) {
if (![taskManager hasTaskWithPath:path]) {
aTask = [[Task alloc] initWithPath:path];
}
else {
return nil;
}
}
else {
return nil;
}
}
else {
return nil;
}
return aTask;
}

// 改写的例子
- (Task *)creatTaskWithPath:(NSString *)path {
if (![path isURL]) {
return nil;
}

if (![fileManager isWritableFileAtPath:path]) {
return nil;
}

if ([taskManager hasTaskWithPath:path]) {
return nil;
}

Task *aTask = [[Task alloc] initWithPath:path];
return aTask;
}

禁止在类的 interface 中定义任何 iVar 成员,只容许使用属性,但能够在特定情形中使用属性生成的 iVar。

尽可能老是使用点操做符访问属性,而不是属性生成的 iVar 变量。如下情形除外:

  • 明确要避免修改产生 KVO 通知的;
  • 需重写属性 getter 或 setter 的;
  • 性能分析肯定使用属性会致使性能不可接受的;
  • 多线程环境中,为防止互斥一次进行多个修改的;
  • init、dealloc 方法中。

动机

若是使用 iVar,不少状况要特殊处理,容易出错。老是使用成员,规则简单,不易出问题。

直接访问 iVar 的 block 会 retain iVar 所属的对象,这点很容易被忽略

定义和使用 iVar 都会产生编译警告,只不过默认设置没启用这两个警告

Property attributes

何时使用 copy?

  • block 属性要定义成 copy。
  • 当一个属性赋值后不指望改变时应当用 copy,最多见的类型如 NSString、NSURL。可变类型的成员,如 NSMutableArray、NSMutableDictionary 不能定成 copy 的。

相关 Demo 可在 https://github.com/BB9z/PropertyTest 得到。

常量

除非调试用的、控制不一样编译模式行为的常量可用宏外,其余常量不得用宏定义。

常量定义示例:

1
2
3
4
5
// 头文件
extern ushort APIFetchPageSizeDefault; // 无const,可在外部修改

// 实现文件
ushort APIFetchPageSizeDefault = 10;

注释

使用Xcode插件VVDocumenter-Xcode能够有效的进行编写注释的需求
全部的.h文件中对外的接口方法定义中必须进行注释,而.m文件中除非已经在.h中已经注释的方法或者是get/set方法能够不注释外,其他函数必须进行注释。
修改代码同时修改相应的注释,以保证注释与代码的一致性。再也不有用的注释要删除。
最后一点,尽可能让代码能够自表述,而不是依赖注释。

注释应该表达那些代码没有表达以及没法表达的东西。若是一段注释被用于解释一些本应该由这段代码本身表达的东西,咱们就应该将这段注释当作一个改变代码结构或编码惯例直至代码能够自我表达的信号。咱们重命名那些糟糕的方法和类名,而不是去修补。咱们选择将长函数中的一些代码段抽取出来造成一些小函数,这些小函数的名字能够表述原代码段的意图,而不是对这些代码段进行注释。尽量的经过代码进行表达。你经过代码所能表达的和你想要表达的全部事情之间的差额将为注释提供了一个合理的候选使用场合。对那些代码没法表达的东西进行注释,而不要仅简单地注释那些代码没有表达的东西。”[^2]

块注释

方法内部禁止使用块注释。除非要临时注释大段代码,通常状况总应使用行注释。

动机

由于块注释不能正确嵌套。

其余

异常

  • 做为被调用模块的维护者,当被调用不当时(参数有问题、不和时宜),如何处理须要考虑(抛出异常仍是返回错误状态);
  • 不要依赖 try catch,它不是代替你作检查、填补遗漏的工具。

参考

相关文章
相关标签/搜索