命名规范 基本原则 命名应该尽可能的清晰和简洁,避免使用单词的简写,避免有歧义的命名
命名遵循驼峰命名法
必须由数字、字母、下滑线组成,不能数字开头。
见名知意(非常重要,严禁出现无意义的命名,如aa,cc,data1,data2.. )
变量名不能重名
变量名不能和系统关键字重名(如int id 等)
代码示范:
1 2 3 4 5 6 7 8 9 10 11 //推荐 insertObject:atIndex: setBackgroundColor: //不推荐 // 不清晰,insert的对象类型和at的位置属性没有说明 insert:at: // 不清晰,不要使用简写 setBkgdColor: // 有歧义,是返回sendPort还是send一个Port? sendPort
但在 Objective-C 编码过程中有部分单词简写非常常用,以至于成为了一种规范,这些简写可以在代码中直接使用,下面部分列举:
1 2 3 4 5 6 7 8 9 10 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
一致性 整个工程的命名风格要保持一致性,最好和苹果SDK的代码保持统一。不同类中完成相似功能的方法应该叫一样的名字,比如我们总是用count
来返回集合的个数,不能在A类中使用count
而在B类中使用getNumber
工程名 工程名的命名必须有 强烈的导向性,让人看到工程名的第一眼就明白该工程对应哪个项目。如 KKLOnLine。 工程名字不能出现中文,不允许有空格。 工程名字采用大驼峰式命名法。
类名 类名的命名遵循 大驼峰式命名法,应该包含一个名词 来表示它代表的对象类型,类名中可以添加 工程的前缀**,防止多个子工程出现类名重复的情况。(如,学习工程中的课程列表STCourseListTableViewController)
方法名 方法名的命名规范遵循 小驼峰式命名法**,如果是私有的方法,在方法面前可以加p_,每个方法名之前需要有详细的注释,标明方法的作用。
可以用一些通用的大写字母缩写打头方法,比如PDF
,TIFF
等。
可以用带下划线的前缀来命名私有方法或者类别中的方法。
若方法表示让对象执行一个动作,使用动词打头来命名(不要使用 do
,does
这种多余的关键字)。
若方法是为了获取对象的一个属性值,直接用属性名称来命名这个方法(不要添加 get
或者其他的动词前缀)。
变量名 变量名的命名规范遵循 小驼峰式命名法(参数名的命名规则和变量名一直)。
常量名 常量名的命名模式 k+大驼峰式命名法形式。(如, kCourseCount**)
头文件 源码的头文件名应该清晰地暗示它的功能和包含的内容。(如:NSBundleAdditions.h )
委托 委托的第一个参数是触发它的对象,第一个关键词是触发对象的类名,除非委托方法只有一个名为sender
的参数。根据委托方法触发的时机和目的,使用 should
, will
, did
等关键词
通知 [触发通知的类名] + [Did | Will] + [动作] + Notification
函数 函数的的命名遵循大驼峰式命名法 ,一般带有缩写前缀,表示方法所在的框架。
如果函数通过指针参数来返回值,需要在函数名中使用 Get
1 const char *NSGetSizeAndAlignment(const char *typePtr, unsigned int *sizep, unsigned int *alignp)
1 BOOL NSDecimalIsNotANumber(const NSDecimal *decimal)
1 2 unsigned int NSEventMaskFromType(NSEventType type) float NSHeight(NSRect aRect)
注释规范 一份规范的代码必定不能缺少详尽的注释。代码块的注释应该更注重说明为什么这么做 ,而不是做了什么
注释的作用:
方便工作交接和引导新同事熟悉代码
方便自己之后回忆代码逻辑
方便生成文档
属性的注释 对于属性我们使用 三杠 /// 进行单行注释
1 2 ///姓名 @property (nonatomic,copy)NSString *name;
特殊行和代码块的注释 对于特殊行和代码块的注释使用 双杠 //
1 2 3 4 5 6 7 8 //设置音量 player.volum = 0.5; if(liveStatus == kLiveStatusEnded){//直播结束的操作 } [[[ResourceTask task] post] subscribeNext:^(ResourceTask *task) {//请求之后的处理 }];
方法的注释 对于方法我们使用多行注释
1 2 3 4 5 6 7 /** @abstract 初始化方法 @param name 姓名 @param sex 性别 @result 返回一个初始化实例 */ - (instancetype)initWithName:(NSString *)name sex:(NSString *)sex;
类的注释 对于类的注释我们使用多行注释 类的声明部分要对当前类的功能作用进行详细的注释,标明类的作用 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // // TopView.h // KKLOnLine // // Created by zahir on 2017/7/31. // Copyright © 2017年 mistong. All rights reserved. // #import <UIKit/UIKit.h> /** @class TopView @abstract TopView 的简单介绍 @discussion TopView 的功能介绍 和 一些特殊的使用注意事项 */ @interface TopView : UIView @end
特定注释
文件的注释 每一个文件都必须 写文件注释,文件注释通常包含
文件所在模块
作者信息
历史版本信息
版权信息
文件包含的内容,作用
注:文件注释的格式通常不作要求,能清晰易读就可以了,但在整个工程中风格要统一。
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 /******************************************************************************* Copyright (C), 2011-2013, Andrew Min Chang File name: AMCCommonLib.h Author: Andrew Chang (Zhang Min) E-mail: LaplaceZhang@126.com Description: This file provide some covenient tool in calling library tools. One can easily include library headers he wants by declaring the corresponding macros. I hope this file is not only a header, but also a useful Linux library note. History: 2012-??-??: On about come date around middle of Year 2012, file created as "commonLib.h" 2012-08- Copyright information: 20: Add shared memory library; add message queue. 2012-08-21: Add socket library (local) 2012-10-10: Change file name as "AMCCommonLib.h" 2012-12-04: Add UDP support in AMC socket library 2013-01-22: Add CFG_LIB_TIMER. 2013-01-22: Remove CFG_LIB_DATA_TYPE because there is already AMCDataTypes.h This file was intended to be under GPL protocol. However, I may use this library in my work as I am an employee. And my company may require me to keep it secret. Therefore, this file is neither open source nor under GPL control. ********************************************************************************/
代码规范 空格
不要在工程里使用 Tab,使用空格来进行缩进。在 Xcode > Preferences > Text Editing
将 Tab 和自动缩进都设置为4 个空格。
+
-
*
/
=
的前后都应该保留一个空格。
逗号,
的前面不应该留有空格,,
后面应该保留一个空格
1 2 3 4 5 6 7 //推荐 NSArray *data = @[@"1", @"2", @"3"]; NSInteger x = 4 * (5 + 3); // 不推荐 NSArray* data=@[ @"1", @"2", @"3" ]; NSInteger x=4*(5+3);
每一行的最大长度 在 Xcode > Preferences > Text Editing > Page guide at column:
中将最大行长设置为80 ,过长的一行代码将会导致可读性问题。
括号的换行 方法 及其他大括号(if
/else
/switch
/while
等),总是在同一行语句打开但在新行中关闭。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 //推荐 - (void)foobar { if (user.age > 18) { // Do something } else { // Do something } } // 不推荐 - (void)foobar { if (user.age > 18) { // Do something } else { // Do something } }
函数的书写和调用
在 -
和 (void)
之间应该有一个空格,第一个大括号 {
的位置在函数所在行的末尾,同样应该有一个空格。
函数有特别多的参数或者名称很长,应该将其按照:
来对齐分行显示。
1 2 3 4 5 6 7 8 9 10 11 - (id)initWithModel:(IPCModle)model ConnectType:(IPCConnectType)connectType Resolution:(IPCResolution)resolution AuthName:(NSString *)authName Password:(NSString *)password MAC:(NSString *)mac AzIp:(NSString *)az_ip AzDns:(NSString *)az_dns Token:(NSString *)token Email:(NSString *)email Delegate:(id<IPCConnectHandlerDelegate>)delegate;
方法内部避免空白行 在方法内部尽量避免空白行,如果为了区分功能,请用单行注释进行分割(注释可以增加斜杠数量////
表明功能隔离)。
闭包建议避免以冒号对齐的方式调用
较短的 block 可以写在一行内。
block 内的代码采用4个空格 的缩进
^
和 (
之间,^
和 {
之间都没有空格,参数列表的右括号 )
和 {
之间有一个空格
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // 较短的block写在一行内 [operation setCompletionBlock:^{ [self onOperationDone]; }]; // 推荐 [UIView animateWithDuration:1.0 animations:^{ // something } completion:^(BOOL finished) { // something }]; //不推荐 [UIView animateWithDuration:1.0 animations:^{ // something } completion:^(BOOL finished) { // something }];
数据结构的语法糖 应该使用可读性更好的语法糖来构造 NSArray
,NSDictionary
等数据结构,避免使用冗长的 alloc
,init
方法。
一行构造,应该在括号两端保留一个空格,使得被构造的元素于与构造语法区分开
1 2 3 4 5 6 7 // 正确,在语法糖的"[]"或者"{}"两端留有空格 NSArray *array = @[ [foo description], @"Another String", [bar description] ]; NSDictionary *dic = @{ NSForegroundColorAttributeName : [NSColor redColor] }; // 不正确,不留有空格降低了可读性 NSArray* array = @[[foo description], [bar description]]; NSDictionary* dic = @{NSForegroundColorAttributeName: [NSColor redColor]};
多行构造,构造元素应保留两个空格来进行缩进,右括号 ]
或者 }
写在新的一行,并且与调用语法糖那行代码的第一个非空字符对齐
1 2 3 4 5 6 7 8 9 10 11 NSArray *array = @[ @"This", @"is", @"an", @"array" ]; NSDictionary *dictionary = @{ NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12], NSForegroundColorAttributeName : fontColor };
构造字典时,字典的Key和Value与中间的冒号:
都要留有一个空格,多行书写时,也可以将Value对齐
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 // 推荐,冒号':'前后留有一个空格 NSDictionary *option1 = @{ NSFontAttributeName : [NSFont fontWithName:@"Helvetica-Bold" size:12], NSForegroundColorAttributeName : fontColor }; // 推荐,按照Value来对齐 NSDictionary *option2 = @{ NSFontAttributeName : [NSFont fontWithName:@"Arial" size:12], NSForegroundColorAttributeName : fontColor }; // 不推荐,冒号前应该有一个空格 NSDictionary *wrong = @{ AKey: @"b", BLongerKey: @"c", }; // 不推荐,每一个元素要么单独成为一行,要么全部写在一行内 NSDictionary *alsoWrong= @{ AKey : @"a", BLongerKey : @"b" }; // 不推荐,在冒号前只能有一个空格,冒号后才可以考虑按照Value对齐 NSDictionary *stillWrong = @{ AKey : @"b", BLongerKey : @"c", };
TODO:待讨论
在方法之间应该有且只有一行。这样有利于在视觉上更清晰。
函数有特别多的参数或者名称很长,应该将其按照:
来对齐分行显示。
应该避免以冒号对齐的方式来调用方法。因为有时方法签名可能有3个以上的冒号和冒号对齐会使代码更加易读。请不要 这样做,尽管冒号对齐的方法包含代码块,因为Xcode的对齐方式令它难以辨认。(?????)
闭包建议避免以冒号对齐的方式调用。
编码规范 编码规范简单来说就是为了保证写出来的代码具备三个原则:可复用, 易维护, 可扩展. 这其实也是面向对象的基本原则.
方法
在方法签名中,应该在方法类型(-/+ 符号)之后有一个空格
一个方法,应该少于50行
方法分布区
life-Cycle、Delegate、Event-Response、Private-Method、Setters-Getters 顺序进行分区域书写 不是delegate方法的,不是event response方法的,不是life cycle方法的,就是private method了。
对于整个单独的功能模块是可以考虑添加category。
同一区域中的方法按照经常:容易忘记的、常使用的程度顺序进行排列
当一个方法参数过多,其实这样就预示着是否我们可以聚合一个model类,一方面代码整洁;另一方面参数过多逻辑不简容易导致错误
1 2 3 4 //推荐 - (void)registerUser(User*)user; //不推荐 -(void)registerUserName:(NSString*)userName password:(NSString*)password email:(NSString*)email;
属性
1 2 3 4 //推荐 @property (nonatomic,strong) NSArray<Teacher *> *teacherList; //不推荐 @property (nonatomic,strong) NSArray *teacherList;
变量
1 2 3 4 5 6 7 8 9 10 11 12 13 //推荐 #define kPeopleNumber 2 if(peopleNumber == kPeopleNumber){ } //或者 int studentNumber = kPeopleNumber; int teacherNumber = kPeopleNumber; //不推荐 if(peopleNumber == 2){ } //或者 int studentNumber = 2; int teacherNumber = 2;
星号表示变量是指针。例如, NSString *text
既不是 NSString* text
也不是 NSString * text
。
尽可能用私有属性代替实例变量的使用。如果有实例变量,要用下划线开头命名NSString *_text
。
1 2 3 4 5 6 7 8 9 10 11 //推荐 @interface GrowthStationVC () @property (nonatomic, strong) NSString *fmTitle; @property (nonatomic, strong) UIView *containerView; @end //不推荐 @interface GrowthStationVC () { NSString *_fmTitle; UIView *containerView; } @end
容器 枚举类型 当使用enum
时,推荐使用NS_ENUM
方式,因为它有更强的类型检查和代码补全。现在SDK有一个宏NS_ENUM()
来帮助和鼓励你使用固定的基本类型。
例如:
1 2 3 4 5 6 7 8 9 10 11 12 //推荐 typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) { RWTLeftMenuTopItemMain = 1, RWTLeftMenuTopItemShows, RWTLeftMenuTopItemSchedule }; //不推荐 enum GlobalConstants { kMaxPinSize = 5, kMaxPinCount = 500, };
布尔值 Objective-C使用YES
和NO
。因为true
和false
应该只在C或C++代码使用。不要拿某样东西直接与YES
比较。
1 2 3 4 5 6 7 8 9 //推荐 if (someObject) {} if (![anotherObject boolValue]) {} //不推荐 if (someObject == nil) {} if ([anotherObject boolValue] == NO) {} if (isAwesome == YES) {} // Never do this. if (isAwesome == true) {} // Never do this.
条件语句
条件语句主体为了防止出错应该使用大括号包围,即使条件语句主体能够不用大括号编写(如,只用一行代码)。这些错误包括添加第二行代码和期望它成为if语句;还有,
even more dangerous defect
可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。除此之外,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。
1 2 3 4 5 6 7 8 9 //推荐 if (!error) { return success; } //不推荐 if (!error) return success; //或: if (!error) return success;
三元操作符 当需要提高代码的清晰性和简洁性时,三元操作符?:
才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if
语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。
Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。
1 2 3 4 5 6 7 8 9 //推荐 NSInteger value = 5; result = (value != 0) ? x : y; BOOL isHorizontal = YES; result = isHorizontal ? x : y; //不推荐 result = a > b ? x = c > d ? c : d : y;
nil检测 在 Objective-C 中向 nil 对象发送命令是不会抛出异常或者导致崩溃的,只是完全的“什么都不干”,所以,只在程序中使用 nil 来做逻辑上的检查。
不要使用诸如 nil == Object
或者 Object == nil
的形式来判断。
1 2 3 4 5 6 7 8 9 // 正确,直接判断 if (!objc) { ... } // 错误,不要使用nil == Object的形式 if (nil == objc) { ... }
使用NSNumber的语法糖 使用带有@
符号的语法糖来生成 NSNumber 对象能使代码更简洁
1 2 NSNumber *phoneNumber = @42; NSNumber *pi_2 = @(M_PI / 2);
NSString 在赋值时被复制 以复制(copy)的方式防止在不知情的情况下 String 的值被其它对象修改。
1 2 3 - (void)setFood:(NSString *)food { _food = [food copy]; }
BOOL的使用
不要 将 BOOL 类型变量直接和 YES
。
BOOL类型可以和 _Bool
,bool
相互转化,但是不能 和 Boolean
转化。
不要将其它类型的值作为 BOOL 来返回,这种情况下,BOOL 变量只会取值的最后一个字节来赋值,这样很可能会取到0(NO)。但是,一些逻辑操作符比如 &&
,||
,!
的返回是可以直接赋给 BOOL。
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 // 错误,无法确定|great|的值是否是YES(1),不要将BOOL值直接与YES比较 BOOL great = [foo isGreat]; if (great == YES) // ...be great! //正确 BOOL great = [foo isGreat]; if (great) // ...be great! // 错误,不要将其它类型转化为BOOL返回 - (BOOL)isBold { return [self fontTraits] & NSFontBoldTrait; } - (BOOL)isValid { return [self stringValue]; } // 正确 - (BOOL)isBold { return ([self fontTraits] & NSFontBoldTrait) ? YES : NO; } // 正确,逻辑操作符可以直接转化为BOOL - (BOOL)isValid { return [self stringValue] != nil; } - (BOOL)isEnabled { return [self isValid] && [self isBold]; }
不要使用 new 方法 尽管很多时候能用new
代替alloc init
方法,但这可能会导致调试内存时出现不可预料的问题。Cocoa的规范就是使用alloc init
方法,使用new
会让一些读者困惑。
避免循环引用 为了保持代码统一性,避免开发中疏忽导致强引用。统一强调在代码块中使用 @weakify(self) ,@strongify(self)
来做处理。
1 2 3 4 5 6 @weakify(self); void(^messageBlock)(NSString *) = ^(NSString *message) { @strongify(self); self.message = message; NSLog(@"%@",message); };