前言
对于代码标准的重要性这里不做过多解释,能看到这篇文章阐明你曾经开始器重代码标准了 ( 代码标准看起来是在限度你的自在和施展,其实它是在间接的帮忙你变得更优良。
)。
适当的代码标准和规范绝不是毁灭代码内容的创造性、优雅性,而是限度适度个性化,以一种广泛认可的对立形式一起做事,进而进步工作效率,升高沟通老本。代码的字里行间流淌着的是软件和程序员的血液,品质的晋升是尽可能少踩坑、杜绝踩反复的坑,切实晋升零碎稳定性,码出品质
依据束缚力度,临时把标准约定为 2 个等级,别离是 必须 和 倡议。
(一)命名标准
1. 通用命名标准
Tips:
所有的命名都应该遵循 3 个根本准则,即“清晰性”、“一致性”、“不要自我指涉”。
- [必须] 清晰性:好的命名应该是能自我形容的。
正例:
removeObject:、[string stringByReplacingOccurrencesOfString:@"1" withString:@"2"]
反例:
remove:(不分明,要删除什么?)、string.replace("1", "2")
(是将 "1" 替换成 "2" 还是将 "2" 替换成 "1"?是将第 1 个 "1" 替换成 "2" 还是将所有的 "1" 都替换成 "2")
-
[必须] 一致性:命名应该和上下文乃至全局放弃一致性,雷同类型或者具备雷同作用的变量的命名形式应该雷同或相似。
正例: NSDictionary、NSArray、NSSet 这几个汇合类都是用 count 来示意数量而不是一个用 count 其它的用 amount 或其余单词,这体现了命名的一致性。@property (readonly) NSUInteger count;
-
[必须] 禁止自我指涉:命名不要自我指涉。告诉、掩码常量等除外(通常指那些能够进行按位运算的枚举值)。
正例: NSString 反例: NSStringObject
- [必须] 杜绝适度缩写,严禁借鉴缩写 (例如把
button
缩写为btn
);国内通用缩写名称除外(例如ATM
、GPS
)。
Tips:
你明确这个缩写的意思不代表其他人也肯定会明确,你的代码可能会被任何人浏览,而浏览的人来自不同的中央承受不同的教育不同的文化,所有倡议个别不要乱应用缩写,只应用那些国内通用缩写。如果为了缩写创立一个缩写对照表只会减少代码浏览复杂度。
正例:
destinationSelection、setBackgroundColor
反例:
destSel、setBgColor
-
[必须] 杜绝无意义的拼音,国内通用名称或者地名人名除外(例如
alibaba
、taobao
、hangzhou
)。反例: DaZhePromotion(打折)
-
[必须] 命名要尽可能的清晰并简洁,如果两者不能兼得,则以清晰为主。
正例: insertObject:atIndex: 反例: insert:at:(不清晰,插入什么?at 代表什么?)
-
[必须] 代码和正文中都要防止应用任何语言的种族歧视性词语。
正例: secondary 反例: slave
-
[必须] 类名、协定名、函数名、常量名、枚举名等一些全局命名须要增加前缀,前缀须要大于 2 个字符且全副大写。
Tips: 零碎保留任意两个字符作为前缀的使用权,包含但不限于 NS、UI、CG、CF、CA、WK、MK、CI、NC;前缀若等于 2 个字符能够思考增加_。正例: ZT_LoginViewController 反例: ZTLoginViewController
- [必须] 类名、协定名、函数名、常量名、枚举名等一些全局命名遵循首字母大写的驼峰命名形式,首个单词是
HTTP
这种非凡词除外。 - [必须] 办法名、属性名等一些非全局命名遵循首字母小写的驼峰命名形式命名,首个单词是
HTTP
这种非凡词除外。 -
[必须] 成员变量须要以_结尾。
正例: NSString *_nameString;
-
[倡议] 在给常量或变量命名时,尽量将示意类型的名词放在词尾,以晋升辨识度。
正例: nameLabel、nameString 反例: name(name 是字符串还是什么?)
-
[倡议] 如果模块、接口、类、办法应用了模式,在命名时尽量体现出具体模式。
正例: OrderFactory、LoginProxy
-
[倡议] 部分长期变量命名能够加上标识符作为前缀。
正例: t_label、t_string(t 在这里示意 temp)
2. 类命名标准
首先作为一个开发者,有一个学习的气氛跟一个交换圈子特地重要,这是一个我的 iOS 开发交换群:130 595 548,不论你是小白还是大牛都欢送入驻,让咱们一起提高,独特倒退!(群内会收费提供一些群主珍藏的收费学习书籍材料以及整顿好的几百道面试题和答案文档!)
-
[必须] 类名命名格调由 ” 前缀 + 类的名称 + 类的类型 ”3 个局部组成,前缀必须大于 2 个字符且全副大写 (如果等于 2 个字符能够增加_);类的名称遵循首字母大写驼峰式命名,类的名称要能表白出该类的性能;类的类型必须应用全称,严禁应用缩写(例如
vc
代替viewController
,cell
代替TableViewCell
),命名形式和名称命名一样首字母大写。正例: WXYZ_LoginViewControler WXYZ_示意前缀,Login 示意该类跟登录相干,ViewController 示意该类是一个视图控制器而不是 View。
3. 办法命名标准
- [必须] 所有办法名称禁止以 new 开始。
-
[必须] 所有办法名称禁止应用_开始。
Tips: 零碎会应用_结尾命名一些零碎公有办法
-
[必须] 外部公有办法须要减少前缀,前缀须要放弃唯一性(例如 p_)。
Tips: 给公有办法加前缀有 2 个益处: 1. 减少辨识度,进步代码可读性。2. 防止本人的办法无意间笼罩了零碎 / 框架同名的公有办法。
-
[必须] 如果办法返回接收者的某个属性值,那么请间接应用属性名作为办法名。
正例: - (CGSize)cellSize; 反例: - (CGSize)getCellSize;
-
[必须] 如果办法间接返回一个或多个值,那么请用 ”
getXXX
“ 命名,这种命名只实用于返回多个数据项的状况。正例: - (void)getCachedResponseForDataTask:(NSURLSessionDataTask *)dataTask completionHandler:(void (^) (NSCachedURLResponse * __nullable cachedResponse))completionHandler;
-
[必须] 办法的每个参数前都必须增加关键字。
正例: - (void)sendAction:(SEL)aSelector toObject:(id)anObject forAllCells:(BOOL)flag; 反例: - (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
-
[倡议] 参数之前的单词尽量能形容参数的意义。
正例: - (id)viewWithTag:(NSInteger)aTag; 反例: - (id)taggedView:(int)aTag;
-
[倡议] 请不要应用“
and
”连贯接收者属性,只管 and 读起来还算顺口,但随着你创立的办法参数的减少,这将会带来一系列的问题。正例: - (int)runModalForDirectory:(NSString *)path file:(NSString *) name types:(NSArray *)fileTypes; 反例: - (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes;
-
[倡议] 如果办法形容了两个独立的动作,则能够应用 ”and” 连接起来。
正例: - (BOOL)openFile:(NSString *)fullPath withApplication:(NSString *)appName andDeactivate:(BOOL)flag;
4. Protocol 命名标准
-
[必须]
Protocol
中的办法命名以触发音讯的对象名结尾,省略类名前缀并首字母小写,如果它没有关联任何类则能够疏忽这个规定。正例: - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)application:(NSApplication *)sender openFile:(NSString *)filename;
-
[必须] 除非
Protocol
办法只有一个参数,否则冒号需紧跟在类名前面。正例: - (BOOL)tableView:(NSTableView *)tableView shouldSelectRow:(int)row; - (BOOL)applicationOpenUntitledFile:(NSApplication *)sender;
5. Category 命名标准
-
[必须] 分类命名也要和类命名一样增加前缀。
正例: UIView (YYAdd) 反例: UIView (Add)
- [必须] 分类中申明的办法名都要加上前缀。
-
[倡议]
Category
中尽量不要申明属性,能挪尽量挪到主类中申明。Tips: 只管从技术上来讲能够在分类中申明属性,然而这么做须要分外小心,因为它很容易呈现内存上或其余一些问题,而且一旦呈现问题很难排查。
- [倡议] 如果一个类比较复杂,那么倡议应用分类组织代码(能够参考零碎的
UIView
)。
6. Notification 命名标准
-
[必须]
Notification
的命名格调由 ” 类名前缀 ” + “ 告诉事件名称 ” + “Notification
“3 个局部组成。正例: UIApplicationDidBecomeActiveNotification UIApplication 示意该告诉属于谁,DidBecomeActive 示意该告诉的作用,Notification 示意它是一个告诉。
-
[倡议] 如果一个类申明了
delegate
属性,通常状况下,这个类的delegate
对象应该能够通过实现的 delegate 办法收到大部分告诉音讯。Tips: 例如 applicationDidBecomeActive: 代理办法和 NSApplicationDidBecomeActiveNotification 告诉(这其实也合乎命名标准的根本准则 "一致性")。
7. 常量命名标准
-
[必须] 如果常量局限于某 ” 编译单元 ” 之内,通常在后面加小写字母 k 作为前缀,若常量在全局可见,通常以类名作为前缀,而后采纳首字母大写的驼峰式命令格调。
正例: // 部分可见 const CGFloat kAnimationDuration = 2.0; // 全局可见 const CGFloat UIActivityIndicatorViewAnimationDuration = 2.0;
8. Exception 命名标准
- [必须] 命令标准和
Notification
一样,把后缀改为 ”Exception
“ 即可。
9. 文件命名标准
- [必须] 文件名全副小写。
- [必须] 采纳_连贯单词。
-
[必须] 命名的格调:” 模块_属性形容 ”,可依据我的项目适当减少形容。
正例: public_back@2x.png
(二)编码标准
1. 通用编码标准
- [必须] 如果有应用到
CF(Core Foundation)
等框架时,或者是在iOS10
以下零碎应用告诉和KVO
时,切记在dealloc
办法中开释对象以及移除告诉和监听。 -
[必须] 在
dealloc
办法内禁止将self
传递进来,如果self
被retain
,到下个runloop
周期再开释则会屡次开释导致crash
。反例: - (void)dealloc {[self unsafeMethod:self]; }
-
[必须] 禁止应用过期的办法或类,应该及时去理解和应用新办法或类。
Tips: 对于过期的办法或类,大都是因为其本身有一些缺点或 BUG 才会不倡议应用,应用新办法时倡议理解一下为什么废除掉旧办法 / 类。
- [必须] 对剪切板的读取操作必须放在子线程中进行,因为用户可能在
Mac
上复制大量数据而后通过iCloud
同步到iPhone
上。 - [必须]
if、else、for、while、case
等前面必须要有{},除非前面是简略的 return 类型语句,例如if (xxx) return;
。 -
[必须] 当办法可能会提前
return
时,须要要留神对象的开释问题,防止内存透露。反例: CFArrayRef arrayRef = (__bridge CFArrayRef)array; if (x == YES) return; CFRelease(arrayRef); 以上代码如果 x 等于 YES 的话那么 arrayRef 对象就会内存透露。
-
[必须] 当应用
@try
解决异样时,须要要留神对象的开释问题,防止内存透露。反例: @try {CFArrayRef arrayRef = (__bridge CFArrayRef)array; do some thing…… CFRelease(arrayRef); } @catch (NSException *exception) { } 以上代码如果 do some thing……出现异常的话那么 arrayRef 就会呈现内存透露。
-
[必须] 申明常量请应用
const
类型申明,禁止应用#define
宏定义。Tips: 宏定义申明常量的毛病: 1. 宏定义只是简略的替换,短少编译查看,运行期可能会呈现溢出或数据谬误等问题。2. 宏定义短少类型,不不便编写文档用例。3. 宏定义可能会被不小心替换。4. 宏定义无奈编写正文。反例: #define kTime @"10" if (1 == 2) {#define kTime @"20"} NSLog(@"time = %@", kTime); 以上代码中的 if 永远不会执行,然而编译器也会将 kTime 替换为 @"20"
-
[倡议] 写一些公共办法时,请尽量应用内联函数或者全局函数而不是宏定义。
Tips: 函数不通过对象调用,所以不会走 OC 的音讯转发流程,效率远高于办法调用;而且函数会有返回值和参数类型以及参数查看,而这些都是宏定义没有的。正例: UIKIT_STATIC_INLINE NSString * kNSStringFromInteger(NSInteger a) {return [NSString stringWithFormat:@"%zd", a]; } 反例: #define kNSStringFromInteger(a) [NSString stringWithFormat:@"%zd", a]
- [必须]
UITableView
应用self-sizing
实现不等高cell
时,请在tableView
:cellForRowAtIndexPath
: 代理办法中给cell
设置数据而不是tableView
:willDisplayCell
:forRowAtIndexPath
: 代理办法中设置数据。 -
[必须] 只在必要的时刻应用懒加载。
Tips: 只在以下三种状况下能力应用懒加载: 1. 对象的创立须要依赖其余对象 2. 对象可能被应用,也可能不被应用 3. 对象创立比拟耗费性能
- [倡议] 懒加载办法内应该只执行须要初始化的操作,不应该有其余不必要的逻辑代码。
-
[必须] 应用一目运算符时左右两边不能有空格。
正例: i++、++i、反例: i ++、++ i
-
[必须] 应用二目、三目运算符时左右两边必须有且仅有一个空格。
正例: 1 + 2 反例: 1+2
- [必须] 采纳 4 个空格缩进,如果要应用 Tab 字符,请将 1 个 Tab 设置成 4 个空格。
- [必须] 应用
NSUserDefaults
存储数据时禁止调用synchronize
办法,因为零碎会在适合的机会将数据保留到本地(即便程序闪退等极其状况)。 -
[必须] 增加到汇合中的对象应该是不可变的,或者在退出之后其哈希码是不可变的。
反例: NSMutableSet *sets = [NSMutableSet set]; NSMutableString *string1 = [NSMutableString stringWithString:@"1"]; [sets addObject:string1]; [sets addObject:@"12"]; [string1 appendString:@"2"]; 当 [string1 appendString:@"2"] 执行完当前 sets 对象内会蕴含 2 个 @"12"。
-
[必须] 必须应用
CGRectGet
获取Frame
的各种值,而不是通过frame.
的形式获取。Tips: CGRect t_frame = CGRectMake(-10, -10, -10, -10); 当一个 view 的 frame 设置成 t_frame 后,其坐标会隐式的转换为 CGRectMake(-20, -20, 10, 10),因为宽高不可能呈现负值;这时通过 t_frame. 的形式获取的值都是谬误的,而 CGRectGet 会主动帮您解决这些隐式转换。正例: CGRectGetWidth(frame)、CGRectGetMinX(frame)、CGRectGetMaxX(frame) 反例: frame.size.width、frame.origin.x、frame.size.width + frame.origin.x
-
[倡议] 单行字符数限度不超过 150 个,超出须要换行(空格能够除外),换行时遵循如下准则:
Tips: 1. 第二行绝对第一行缩进 4 个空格,从第三行起不再持续缩进。2. 运算符与下文一起换行。3. 办法调用的点符号与下文一起换行。正例: - (void)setImageWithURL:(nullable NSURL *)imageURL placeholder:(nullable UIImage *)placeholder options:(YYWebImageOptions)options progress:(nullable YYWebImageProgressBlock)progress ransform:(nullable YYWebImageTransformBlock)transform completion:(nullable YYWebImageCompletionBlock)completion;
- [倡议] 不可变对象尽量应用
copy
润饰,如果重写应用copy
润饰的set
办法,请留神调用copy
办法。 - [倡议] 对于一些体积小并且重要的信息,不要频繁的存储到本地,能够应用
NSUserDefaults
进行存储。它会在适合的机会存储到本地,这防止了频繁的写入操作,而且在某些极其状况下它也能保证数据存储到本地(例如程序闪退等状况)。 - [倡议] 在多线程环境下审慎应用可变汇合,必要时候能够采纳加锁或
GCD
的同步线程进行爱护,或者在拜访可变汇合时先将其copy
为不可变对象而后再对其拜访。 - [倡议] 头文件中尽量不要申明成员变量而是应用属性代替。
-
[倡议] 头文件中的属性尽量申明为只读,能够在实现文件中再将属性申明为可读可写。
正例: @interface WXYZModel : NSObject @property (nonatomic, readonly) NSString *name; @end @interface WXYZModel () @property (nonatomic, strong) NSString *name; @end
-
[倡议] 不要应用一个类去保护多个类的内容,例如一个常量类保护所有的常量类,要按常量性能进行归类,离开保护。
Tips: 大而全的类,横七竖八,应用查找性能能力定位到具体位置,不利于了解也不利于保护。正例: 缓存相干常量类放在 CacheCosts 下,系统配置相干常量类放在 SystemConfigConsts 下。
- [倡议] 如果大括号内为空,则简洁的写成 {} 就行。
-
[倡议] 没有必要减少多余空格来使高低代码的等号对齐。
正例: int a1 = 1; long a2 = 3; NSString *a3 = @""; 反例: int a1 = 1; long a2 = 3; NSString *a3 = @"";
-
[倡议] 少用
if else
,能够应用if return
替换,if
嵌套最好不超过 5 层。正例: if (x == 1) { …… return; } if (x == 2) { …… return; } 反例: if (x == 1) {……} else if (x == 2) {……}
-
[倡议] 尽量避免采纳取反逻辑运算符,因为取反逻辑不利于疾速了解。
正例: if (array == nil) {……} 反例: if (!array) {……}
- [倡议] 如果用到了很多协定,必要时能够把协定封装到一个独自的头文件中,这样做不仅能够减小编译工夫,还能防止循环援用。
- [倡议] 应用 Switch 枚举时尽量将所有枚举类型都列举进去而不应用
default
,这样下次减少枚举类型时如果Switch
没有解决会有正告信息。 -
[倡议] 尽量应用字面量语法创建对象,少用与之等价的办法。
Tips: OC 中的 NSArray、NSString、NSDictionay、NSNumber 都有与之对应的字面量语法: @[]、@""、@{}、@();应用它们有以下长处: 1. 简略易读,进步代码的可读性和可维护性。2. 应用字面量创立数组、字典时如果元素里在 nil 则会抛出异样,而应用 arrayWithObjects: 这些等价办法创立则会失落 nil 后的数据,抛出异样能让你晓得这里有问题及时批改避免问题在线上产生。毛病: 1. 应用字面量创立的对象默认是不可变的,如果要创立可变对象须要进行 mutableCopy 操作。2. 不反对子类,如果你创立了一个 NSString 的子类,@"" 并不会返回你想要的子类对象。
- [倡议] 头文件中尽量少援用其余头文件,尽量应用
@class
向前申明,每次引入其余头文件时问问本人是否必须要这样做。 - [倡议]
UI
控件倡议应用weak
润饰而不是strong
润饰。
2. 类编码标准
- [必须] 如果超类的某个初始化办法不适用于子类,那么子类肯定要覆写超类的这个办法并解决该问题或抛出异样。
- [倡议] 尽量不要应用
load
类办法,如果必须要应用不能在办法内实现简单逻辑或梗塞线程。 - [倡议] 尽量减少继承,类的继承尽量不要超过 3 层,必要时刻能够思考用分类、协定来代替继承。
- [倡议] 把一些稳固的、公共的变量或者办法抽取到父类中。子类尽量只维持父类所不具备的个性和性能。
3. 办法编码标准
-
[必须] 禁止在
init
等初始化办法外部、getter
、setter
、dealloc
或其余非凡中央应用. 语法拜访属性。Tips: 当存在继承关系时应用. 语法拜访会因为多态关系调用子类的实现办法,而如果这个时候子类还没有初始化好或者曾经开释了那么可能会呈现一些奇怪的问题。
-
[必须] 办法参数在定义和传入时,逗号前面必须增加一个空格。
正例: method(a1, a2, a3);
- [倡议] 单个办法的行数倡议不超过 80 行,正文、左右大括号、空行、回车等除外。
- [倡议] 在实现文件外部也尽量应用. 语法拜访属性而不是应用_间接拜访成员变量来保障格调对立。
4. Block 编码标准
-
[必须] 调用
Block
必须判空解决。Tips: 对于简略的 Block 能够应用三目运算进行判空解决,例如 !self.block ?: self.block();
-
[必须] 在
Block
外部应用内部变量时要留神循环援用的问题。Tips: 1. 不肯定在 Block 内应用 self 才会循环援用,如下状况也会造成循环援用: - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { WXYZ_TitleTableViewCell *cell = ……… cell.refreshTableViewBlock = ^{[tableView reloadData]; }; return cell; } 2. Block 外部是否要应用 weak 须要看 Block 自身和 weak 的这个对象是否存在间接或间接的互相援用,若无互相援用则不须要应用 weak。3. 如果 Block 外部应用了 strong 润饰了内部的 weak 变量,那么当应用 strong 指向成员变量时须要进行判空,否则会解体,参考以下代码: __weak typeof(self) weakSelf = self; cell.refreshTableViewBlock = ^{__strong typeof(weakSelf) strongSelf = weakSelf; if (strongSelf != nil) {strongSelf->_name = @"name";} }; 如果把 (strongSelf != nil) 的判断去掉那么可能会解体。
5. 告诉编码标准
- [必须] 在发送告诉时,请应用
userInfo
进行传值,而不是object
。 - [必须] 告诉核心是以同步的形式发送申请的,所以不要在告诉办法做一些简单的计算,特地是当它处于主线程的时候,如果想发送异步告诉能够应用
NSNotificationQueue
。 - [倡议] 在工程里能不必告诉尽量不必告诉,告诉尽管灵便弱小,然而如果滥用会导致工程质量降落,呈现问题时也比拟难排查。
6. 正文编码标准
- [必须] 与其搜索枯肠写正文,不如想想怎么命名;正文是起辅助作用的,好的命名应该是能自我解释的,如果命名能够解释其作用,并且办法没有任何副作用或者注意事项,那么就不必写正文;正文应该帮忙他人更快的了解该办法的应用和注意事项,如果该办法有须要留神的中央肯定要在正文中体现进去。
- [必须] 当批改了办法实现时须要同步批改正文内容。
- [必须] 正文不要写的太简短,要简略易读容易了解。
-
[必须] 正文的双斜线和内容之间有且仅有一个空格。
正例: // 这是示例正文,请留神在双斜线后有一个空格 - (void)testFunction;
- [必须] 对于代码正文需谨慎,代码被正文个别有 2 种可能,1) 后续会复原此段代码逻辑;2) 永恒不必;对于第 1 种状况需增加相应正文,如果没有正文信息难以通晓正文动机,后者倡议间接删除。如果有须要能够通过代码仓库查阅历史代码。
-
[必须] 应用非凡正文标记时,请注明标记人和标记工夫,留神及时处理这些标记。
正例: /** * @brief 简要形容 * @author 表明开发该类模块的作者 */ // FIXME: 有 bug,须要批改 - (void)testFunction;
-
[倡议] 别给蹩脚的代码加正文,重构它。
Tips: 正文不能丑化蹩脚的代码。当希图应用正文前,先思考是否能够通过调整结构,命名等操作,打消写正文的必要。
(三)工程构造标准
- [必须] 部分应用的常量、动态变量申明在
@interface
之前。 -
[必须]
@property
同一类型的申明放在一块,不同类型的申明用 2 行空格隔开。正例: @interface MineViewController () @property (nonatomic, weak) UIView *headView; @property (nonatomic, weak) UITableView *tableView; 我是换行符,请疏忽 @property (nonatomic, copy) NSArray *dataSourceArray;
-
[必须] 不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开以晋升可读性。
正例: [self createSubviews]; [self createTableview]; [self netRequest];
-
[必须] 办法归类
#pragma mark - LifeCycle(生命周期相干的代码放在最下面) - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} #pragma mark - Public(公开办法) // code... // 上空一行 // 下空两行 #pragma mark - Private(公有办法) #pragma mark - Override(须要笼罩父类的办法) #pragma mark - Notification(告诉办法) #pragma mark - Delegate(Delegate 须要实现的办法) #pragma mark - getter/setter
结语
这只是一篇对于 iOS 的代码标准,所以某些须要和服务端须要对立的标准 (例如错误码) 并没有提到,还有些对于如何编写平安代码方面的标准也只是稍微提到,因为对于如何写出更平安的代码应该不属于代码标准层面;欢送大家提出更好的倡议或改良,我也会不断更新欠缺;最初祝大家码出开心,码出品质。