iOS团队代码规范(OC版)

文章出处 https://HawkingOuYang.github.io/

我的GitHub


项目工程目录

待整理

Modules:存放功能模块。将程序划分为多个功能模块,每个模块基于MVC/MVVM组织代码

Library:存放三方库

Util:存放自己封装的工具类

Catergory:存放扩展类

Custom:存放自定义的其他类

MacroAndConstant:存放宏定义和常量的文件

BaseViewController:基类,单独提出来

SupportingFile:存放项目配置文件

Resource:存放资源,按照图片,音频,视频不同分类存放

Request:封装的网络请求类

总体命名规范(遵循苹果官方规范)

小驼峰命名法(CamelCase):第一个单词小写字母开头,其他单词首字母大写;

大驼峰命名法(PascalCase):所有首字母大写。

1、类名、协议名:遵循大驼峰命名法;

2、常量:这里的常量指的是宏(#define)、枚举(enum)、常量(const)等,名称遵循大驼峰命名法;

3、方法

方法名和方法参数遵循相同的规则,使用小写开头的小驼峰法;

方法名和参数尽量读起来像是一句话;

方法名不允许使用“get“前缀;

如果方法是为了获取对象的一个属性值,直接用属性名称来命名方法,不要添加 get 或其他的动词:

不要使用 do 或 does 作为名字的一部分,动词本身的暗示就足够了;

-或+与返回类型间留一个空格,但参数列表之间不要留间隔;

如果参数过多,推荐每个参数各占一行;

如果方法名以一个众所周知的大写缩略词开始,该规则不适用;

避免使用下划线作为前缀,苹果公司保留这种使用,由第三方使用可能会导致命名冲突;

4、变量:

类成员变量,属性,局部变量,使用小写开头的小驼峰法,其中类成员变量在名称最前面加一个下划线,比如:myLovalVariable, _myInstanceVariable;变量名的名称尽量可以推测其用途,具有描述性。

5、方法的参数命名:

不要再方法名中使用类似 pointer,ptr这样的字眼去表示指针,参数本身的类型足以说明;

不要使用简写,拼出完整的单词;

6、前缀

前缀是编程接口命名的重要部分,它们区分了软件的不同功能区域:

前缀可以防止第三方开发者与Apple的命名冲突;

同样可以防止Apple内部的命名冲突;

前缀有指定格式:

它由二到三个大写字母组成,不使用下划线和子前缀,

命名类、协议、常量和typedef结构体时使用前缀。

方法名不使用前缀(因为它存在于特定类的命名空间中),私有方法可以加前缀,比如private_XXX。

结构体字段不使用前缀。

语言

应该使用US英语.

应该:

1
UIColor *myColor = [UIColor whiteColor];

不应该:

1
UIColor *myColour = [UIColor whiteColor];

代码注释

方法、函数、类、协议、类别的定义都需要注释,推荐采用Apple的标准注释风格,好处是可以在引用的地方alt+点击自动弹出注释,非常方便。

有很多可以自动生成注释格式的插件,推荐使用VVDocumenter。

头文件

声明一个孤立的class或protocol

将声明放入单独的文件

使头文件名与声明的class/protocol相同

声明关联的class或protocol

将关联的声明(class/category/protocol)放入同一个头文件

头文件名与主要的class/category/protocol相同

Framework头文件

每个framework都应该有一个同名头文件

Include了框架内其他所有头文件

声明属性和实例变量

如果property表⽰示为⼀个名词或动词,格式如下@property (…) 类型 名词/动词 ;

1
2
3
@property (strong) NSString *title;
@property (assign) BOOL showsAlpha;

如果property表⽰示为一个形容词 可省略 ”is” 前缀,但要指定getter⽅方法的惯⽤用名称

1
@property (assign, getter=isEditable) BOOL editable;

实例变量

通常不应该直接访问实例变量 init、dealloc、accessor methods等⽅方法内部例外 实例变量以下划线 “_” 开始 确保实例变量描述了所存储的属性

1
2
3
@implementation MyClass {
BOOL _showsTitle;
}

如果想要修改property的实例变量名,使用 @synthesize语句

1
2
3
@implementation MyClass
@synthesize showsTitle=_showsTitle;

为一个class添加实例变量时,有几点需要注意:

避免声明公有实例变量 开发者关注的应该是对象接口,⽽不是其数据细节 你可以通过使用property来避免声明实例变量 如果需要声明实例变量,指定关键字@private 或 @protected 如果你希望⼦子类可以直接访问某个实例变量,使⽤用 @protected 关键字 如果⼀个实例变量是某个类可访问的属性,确保写了accessor methods 如果有可能,还是使用property

可变对象

你应该同时避免暴露在公开的接口中可变的对象,因为这允许你的类的使用者改变类自己的内部表示并且破坏类的封装。你可以提供可以只读的属性来返回你对象的不可变的副本。

1
2
3
4
5
6
7
/* .h */
@property (nonatomic, readonly) NSArray *elements
/* .m */
- (NSArray *)elements {
return [self.mutableElements copy];
}

##常量

Integer常量使⽤枚举

使⽤枚举来关联一组integer常量 枚举常量和typedef遵循函数的命名规范,下⾯面的例⼦子是 NSMatrix.h

1
2
3
4
5
6
typedef enum _NSMatrixMode {
NSRadioModeMatrix = 0,
NSHighlightModeMatrix = 1,
NSListModeMatrix = 2,
NSTrackModeMatrix = 3
} NSMatrixMode;

Typedef 标记(在上述⽰示例中的 _NSMatrixMode)不是必要的。你可以为bit masks之类的东西创建⼀个匿名枚举

1
2
3
4
5
6
7
enum {
NSBorderlessWindowMask = 0,
NSTitledWindowMask = 1 << 0,
NSClosableWindowMask = 1 << 1,
NSMiniaturizableWindowMask = 1 << 2,
NSResizableWindowMask = 1 << 3, -->
};

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

1
2
3
4
5
typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
RWTLeftMenuTopItemMain,
RWTLeftMenuTopItemShows,
RWTLeftMenuTopItemSchedule
};

float常量使用const修饰符

1
const float NSLightGray;

其他类型的常量

通常不应使用 #define 预编译指令来创建常量;

对#define预编译指令,⼤写所有字母;

定义NSString常量来作为Notification和Key值,这样做可以有效防⽌止拼写错误。

应该

1
2
static NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";
static CGFloat const RWTImageThumbnailHeight = 50.0;

不应该

1
2
#define CompanyName @"RayWenderlich.com"
#define thumbnailHeight 2

通知

Notification的格式 类/头⽂文件名+进⾏行状态+通知名称+Notification

1
[Name of associated class] + [Did | Will] + [UniquePartOfName] + Notification

如:

1
2
3
4
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
NSColorPanelColorDidChangeNotification

方法大括号和其他大括号(if/else/switch/while 等.)总是在同⼀行语句打开但在新⾏中关闭。

应该

1
2
3
4
5
if (user.isHappy) {
//Do something
} else {
//Do something else
}

#如果⼀个方法有特别多的参数或者名称很长,应该将其按照 : 来对齐分行显⽰

1
2
3
4
-(id)initWithModel:(IPCModle)model
resolution :(IPCResolution)resolution
authName :(NSString *)authName
password :(NSString *)password

私有方法

不要使用下划线作为私有方法的前缀,Apple保留这一使用方式。

因为若是你的私有方法名已被Apple使用,覆盖它将会产生极难追踪的BUG。

如果继承自大型Cocoa框架(如UIView),请确保子类的私有方法名与父类不一样。

可以为私有方法加一个前缀,如公司名或项目名:XX_,

例如你的项目叫做Byte Flogger,那么前缀可能是:BF_addObject。

总之,为子类的私有方法添加前缀是为了不覆盖其父类的私有方法

语法糖

如果构造代码写在⼀行,需要在括号两端留有⼀个空格,使得被构造的元素于与构造语法区分开来:

// 正确,在语法糖的 “[]” 或者 “{}” 两端留有空格

1
NSArray *array = @[ [foo description], @"Another String", [bar description] ]; NSDictionary *dict = @{ NSForegroundColorAttributeName : [NSColor redColor] };

// 不正确,不留有空格降低了可读性

1
2
NSArray* array = @[[foo description], [bar description]];
NSDictionary* dict = @{NSForegroundColorAttributeName: [NSColor redColor]};

如果构造代码不写在⼀行内,构造元素需要使⽤四个空格来进行缩进,右括号 ] 或者 } 写在新的⼀行,并且与调⽤语法糖那⾏代码的第一个非空字符对齐:

1
2
3
4
5
6
NSArray *array = @[
@"This",
@"is",
@"an",
@"array"
];

1
2
3
4
NSDictionary *dictionary = @{
NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12],
NSForegroundColorAttributeName : fontColor
};

构造字典时,字典的 Key 和 Value 与中间的冒号 : 都要留有一个空格。

代码组织和分类

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

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
#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