代码规范
Posted 「违规用户」
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了代码规范相关的知识,希望对你有一定的参考价值。
原文:https://github.com/cncnTech/ios-development-guideline#%E5%9F%BA%E6%9C%AC%E5%8E%9F%E5%88%99
Objective-C 编码规范
目录
1. Objective-C 编码规范及原则
代码格式
项目内使用 Clang Format
来约束 C/C++/Objective-C 编码风格
Clang Format 配置步骤(Xcode):
- Xcode 安装
ClangFormat-Xcode
插件(https://github.com/travisjeffery/ClangFormat-Xcode) - 配置快捷键,
Xcode -> Edit -> Format Code
,勾选使用File
使用根目录下的.clang-format
作为约束文件,勾选Enable Format on Save
- 将本文档同目录下的
.clang-format
文件放入新建工程的根目录下,命名为.clang-format
- 后续在 Xcode 保存该工程的代码文件时,会自动根据
.clang-format
来规范代码格式
排序
包括头文件引用
,工程 group 排序
,无特殊说明时,按字母顺序排序。每次工程中添加新 group 时,需重新按字母排序。
需要额外注意的事项
clang-format 在部分格式上不支持强制在 //
后保留一个空格,需要在写代码时额外注意。
// Preferred:
// 这里是注释
// Not Preferred:
//这里是注释
在使用 block 时,应尽量通过拆分和换行,减少 block 内代码块的整体缩进,尽量控制在小宽度屏幕也能比较方便的阅读代码
// Preferred: MSTClientCompletionHandler handler = ^(NSInteger code, NSString *msg, id data) // do something. ; [[MSTClient sharedInstance] fetchPhoneCertCodeWithPhone:telephone completionHandler:^(NSInteger code, NSString *msg, id data) handler(code, msg, data); ]; // Preferred: [[MSTClient sharedInstance] fetchPhoneCertCodeWithPhone:telephone completionHandler:^(NSInteger code, NSString *msg, id data) // do something. ]; // Not Preferred: [[MSTClient sharedInstance] fetchPhoneCertCodeWithPhone:self.telephone completionHandler:^(NSInteger code, NSString *msg, id data) // do something. ];
例外
在宏定义文件(eg. MSTMacro.h)中,需要的代码格式不同于其他文件,使用clang-format off
来关闭 clang-format
// clang-format off
// 非标准代码风格的代码
// clang-format on
空行
- #import 块前后保留一个空行
- @interface 与 @protocol 间保留一个空行
- 多个 @property 时,如果涉及不同区块、不同职能属性,可使用空行分隔成多组
- .m 文件中 @interface 与 @implementation 使用两个空行隔开
- 方法间使用一个空行分隔
- @end 之后保留一个空行
- 方法内部空行一般用于分隔两个相对独立的代码区块,不要随意添加无意义的空行
// Preferred: #import <UIKit/UIKit.h> @class MSTHotelSearchParams; @class MSTHotelList; @interface MSTHotelSearchResultChildViewController : UITableViewController @property (strong, nonatomic) MSTHotelList *hotels; - (id)initWithSearchParams:(MSTHotelSearchParams )params; - (void)configureWithSearchParams:(MSTHotelSearchParams *)params; @end // Not Preferred: #import <UIKit/UIKit.h> @class MSTHotelSearchParams; @class MSTHotelList; @interface MSTHotelSearchResultChildViewController : UITableViewController @property (strong, nonatomic) MSTHotelList *hotels; - (id)initWithSearchParams:(MSTHotelSearchParams *)params; - (void)configureWithSearchParams:(MSTHotelSearchParams *)params; @end
ViewController.m 文件结构
使用 #pragma mark 区分不同代码块,可引用Code Snippets For Xcode in cncn.com & cncn.net,code snippet 来快速生成结构,一个典型的 ViewController.m 结构如下:
///-------------------------------------- #pragma mark - life cycle ///-------------------------------------- - (void)viewDidLoad [super viewDidLoad]; ///-------------------------------------- #pragma mark - setup & configuration ///-------------------------------------- ///-------------------------------------- #pragma mark - custom delegate ///-------------------------------------- ///-------------------------------------- #pragma mark - event response ///-------------------------------------- ///-------------------------------------- #pragma mark - update views ///-------------------------------------- ///-------------------------------------- #pragma mark - helper/private methods ///-------------------------------------- ///-------------------------------------- #pragma mark - getters and setters ///--------------------------------------
命名原则
基本原则
- 清晰
命名应该尽可能的清晰和简洁,但在Objective-C中,清晰比简洁更重要。由于Xcode强大的自动补全功能,我们不必担心名称过长的问题。
// Preferred: insertObject:atIndex: // Not Preferred: insert的对象类型和at的位置属性没有说明 insert:at: // Preferred: removeObjectAtIndex: // Not Preferred:,remove的对象类型没有说明,参数的作用没有说明 remove:
除官方推荐的缩写外,尽量不使用单词缩写,拼写出完整的单词:
// Preferred: destinationSelection setBackgroundColor: // Not Preferred: 不要使用简写 destSel setBkgdColor:
有部分单词简写在Objective-C编码过程中是非常常用的,以至于成为了一种规范,这些简写可以在代码中直接使用,下面列举了部分:
alloc == Allocate max == Maximum
alt == Alternate min == Minimum
app == Application msg == Message
calc == Calculate nib == Interface Builder archive
dealloc == Deallocate pboard == Pasteboard
func == Function rect == Rectangle
horiz == Horizontal Rep == Representation (used in class name such as NSBitmapImageRep).
info == Information temp == Temporary
init == Initialize vert == Vertical
int == Integer
命名方法或者函数时要避免歧义
// Not Preferred: 有歧义,是返回sendPort还是send一个Port?
sendPort
// Not Preferred: 有歧义,是返回一个名字属性的值还是display一个name的动作?
displayName
- 一致性
使用前缀
如果代码需要打包成Framework给别的工程使用,或者工程项目非常庞大,需要拆分成不同的模块,使用命名前缀是非常有用的。
- 前缀由大写的字母缩写组成,比如Cocoa中NS前缀代表Founation框架中的类,IB则代表Interface Builder框架。
- 可以在为类、协议、函数、常量以及typedef宏命名的时候使用前缀,但注意不要为成员变量或者方法使用前缀,因为他们本身就包含在类的命名空间内。
- 命名前缀的时候不要和苹果SDK框架冲突。
项目的工程命名尽量不直接使用业务名称的拼音或者对应的英文名(业务在后续中可能会变化发展),例如旅游顾问项目的项目代号为 Mansinthe, 前缀为 MST。
现有的基础类库从旅游顾问项目中拆分,仍以 MST
为前缀。
命名类和协议(Class&Protocol)
类名以大写字母开头,应该包含一个名词来表示它代表的对象类型,同时可以加上必要的前缀,比如NSString
, NSDate
,NSScanner
, NSApplication
等等。
而协议名称应该清晰地表示它所执行的行为,而且要和类名区别开来,所以通常使用ing
词尾来命名一个协议,比如NSCopying
,NSLocking
。
有些协议本身包含了很多不相关的功能,主要用来为某一特定类服务,这时候可以直接用类名来命名这个协议,比如NSObject
协议,它包含了id对象在生存周期内的一系列方法。
命名头文件(Headers)
源码的头文件名应该清晰地暗示它的功能和包含的内容:
-
如果头文件内只定义了单个类或者协议,直接用类名或者协议名来命名头文件,比如
NSLocale.h
定义了NSLocale
类。 -
如果头文件内定义了一系列的类、协议、类别,使用其中最主要的类名来命名头文件,比如
NSString.h
定义了NSString
和NSMutableString
。 -
每一个Framework都应该有一个和框架同名的头文件,包含了框架中所有公共类头文件的引用,比如
Foundation.h
-
Framework中有时候会实现在别的框架中类的类别扩展,这样的文件通常使用
被扩展的框架名
+Additions
的方式来命名,比如NSBundleAdditions.h
。
命名方法(Methods)
Objective-C的方法名通常都比较长,这是为了让程序有更好地可读性,按苹果的说法“好的方法名应当可以以一个句子的形式朗读出来”。
方法一般以小写字母打头,每一个后续的单词首字母大写,方法名中不应该有标点符号(包括下划线),有两个例外:
- 可以用一些通用的大写字母缩写打头方法,比如
PDF
,TIFF
等。 - 可以用带下划线的前缀来命名私有方法或者 Category 中的方法。
如果方法表示让对象执行一个动作,使用动词打头来命名,注意不要使用do
,does
这种多余的关键字,动词本身的暗示就足够了:
// 动词打头的方法表示让对象执行一个动作 - (void)invokeWithTarget:(id)target; - (void)selectTabViewItem:(NSTabViewItem *)tabViewItem;
如果方法是为了获取对象的一个属性值,直接用属性名称来命名这个方法,注意不要添加get
或者其他的动词前缀:
// Preferred: 使用属性名来命名方法 - (NSSize)cellSize; // Not Preferred: 添加了多余的动词前缀 - (NSSize)calcCellSize; - (NSSize)getCellSize;
对于有多个参数的方法,务必在每一个参数前都添加关键词,关键词应当清晰说明参数的作用:
// Preferred: 保证每个参数都有关键词修饰 - (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; // Not Preferred: 遗漏关键词 - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag; // Preferred: - (id)viewWithTag:(NSInteger)aTag; // Not Preferred: 关键词的作用不清晰 - (id)taggedView:(int)aTag;
不要用and
来连接两个参数,通常and
用来表示方法执行了两个相对独立的操作(从设计上来说,这时候应该拆分成两个独立的方法):
// Not Preferred: 不要使用"and"来连接参数 - (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; // Preferred: 使用"and"来表示两个相对独立的操作 - (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
方法的参数命名也有一些需要注意的地方:
- 和方法名类似,参数的第一个字母小写,后面的每一个单词首字母大写
- 不要再方法名中使用类似
pointer
,ptr
这样的字眼去表示指针,参数本身的类型足以说明 - 不要使用只有一两个字母的参数名
- 不要使用简写,拼出完整的单词
下面列举了一些常用参数名:
...action:(SEL)aSelector ...alignment:(int)mode ...atIndex:(int)index ...content:(NSRect)aRect ...doubleValue:(double)aDouble ...floatValue:(float)aFloat ...font:(NSFont *)fontObj ...frame:(NSRect)frameRect ...intValue:(int)anInt ...keyEquivalent:(NSString *)charCode ...length:(int)numBytes ...point:(NSPoint)aPoint ...stringValue:(NSString *)aString ...tag:(int)anInt ...target:(id)anObject ...title:(NSString *)aString
存取方法(Accessor Methods)
存取方法是指用来获取和设置类属性值的方法,属性的不同类型,对应着不同的存取方法规范:
// 属性是一个名词时的存取方法范式 - (type)noun; - (void)setNoun:(type)aNoun; // 栗子 - (NSString *)title; - (void)setTitle:(NSString *)aTitle; // 属性是一个形容词时存取方法的范式 - (BOOL)isAdjective; - (以上是关于代码规范的主要内容,如果未能解决你的问题,请参考以下文章