iOS开发之代码规范
2016-01-13 11:02
429 查看
作为一个有轻微洁癖的iOS开发工程师,经常会看到杂乱不整洁的代码,什么等号左右不空格,类名首字母小写,方法名首字母大写;依赖乱七八糟;在view里写业务,在view里发网络请求。看到这样的代码,我当时心里是崩溃的。
由于不良的代码习惯和编程规范或多或少会引起一些意想不到的Bug,由此可见一个好的编码习惯是多么的重要!
以下是我将自己所认为的一些比较好的代码规范,跟大家分享一下。
命名规范
iOS命名有两大原则:可读性和防止命名冲突(通过加前缀来防止)。Objective-C 的命名通常都比较长,遵循驼峰式命名法。一个好的命名标准很简单,就是做到在开发者一看到名字时, 就能够懂得它的含义和使用方法。
1. 类的命名:每个模块都要加上自己的前缀,前缀在编程接口中非常重要,可以区分软件的功能范畴并防止不同文件或者类之间命名发生冲突。如:
相册模块(PhotoGallery)的代码都以PG作为前缀:PGAlbumViewController, PGDataManager。
2. 常量的命名:
对于常量的命名最好在前面加上字母k作为标记. 如:
定义作为NSDictionary或者Notification等的Key值字符串时加上const关键字, 以防止被修改. 如:
I. 若常量作用域超出编译单元(实现文件), 需要在类外可见时, 使用extern关键字, 并加上该类名作为前缀. 如 extern NSString *const PGThumbnailSize
II.全局常量(通知或者关键字等)尽量用const来定义. 因为如果使用宏定义, 一来宏可能被重定义. 二来引用不同的文件可能会导致宏的不同. P.S. 对于#define也添加一下前缀k(强迫症, 哈哈...)
3.变量和对象的命名
给一个对象命名时建议采用修饰+类型的方式. 如果只用修饰命名会引起歧义, 比如title (这个到底是个NSString还是UILabel?). 同样的, 如果只用类型来命名则会缺失作用信息, 比如label (好吧, 我知道你是个UILabel, 但是我不知道它是用来做什么的呀?). So, 正确的命名方式为:
对于BOOL类型, 应加上is前缀, 比如- (BOOL)isEqualToString:(NSString *)aString这样会更加清晰. 如果某方法返回非属性的 BOOL 值, 那么应根据其功能, 选用 has 或 is 当前缀, 如- (BOOL)hasPrefix:(NSString
*)aString
如果某个命名已经很明确了, 为了简洁可以省去类型名. 比如scores, 很明显是个array了, 就不必命名成scoreArray了
编码规范
所谓写规范的代码就是写别人能看得懂的代码,有一句话是这么说的,“复杂的代码往往都是新手所写,只有经验老道的高手才能写出简单,富有表现力的代码”。
代码越少,Bug也越少,没有重复逻辑的代码更易于维护,当你修复了一个bug,如果相同的逻辑还出现在另外一个地方,而你没意识到,你有没有觉得很冤?
以下是一些优秀程序员总结的代码规范,受益匪浅~~~
1). 判断nil或者YES/NO
Preferred:
Not preferred:
if (someObject == YES)容易误写成赋值语句, 自己给自己挖坑了...而且if (someObject)写法很简洁, 何乐而不为呢?
2). 条件赋值
Preferred:
Not preferred:
如果是存在就赋值本身, 那就可以这样简写, 多简洁啊, 哈哈...
3). 初始化方法
Preferred:
第一个好处还是简洁, 第二个好处是可以防止初始化进去nil值造成crash
4). 定义属性
Preferred:
建议定义属性的时候把所有的参数写全, 尤其是如果想定义成只读的(防止外面修改)那一定要加上readonly, 这也是代码安全性的一个习惯.
如果是内部使用的属性, 那么就定义成私有的属性(定义到.m的class extension里面)
对于拥有Mutable子类型的对象(e.g. NSString, NSArray, NSDictionary)一定要定义成copy属性. Why? 示例: NSArray的array = NSMutableArray的mArray; 如果mArray在某个地方改变了, 那array也会跟着改变. So, make sense?
尽量不要暴露mutable类型的对象在public interface, 建议在.h定义一个Inmutable类型的属性, 然后在.m的get函数里面返回一个内部定义的mutable变量. Why? For security as well!
5). BOOL赋值
Preferred:
Not preferred:
为什么要这么写呢, 我不告诉你, 哈哈哈...
6) 拒绝死值
Preferred:
Not preferred:
死值每次修改的时候容易被遗忘, 地方多了找起来就悲剧了. 而且定义成枚举或者static可以让错误发生在编译阶段. 另外仅仅看到一个数字, 完全不知道这个数字代表的意义. 纳尼?
7). 复杂的条件判断
Preferred:
Not preferred:
清晰明了, 每个函数DO ONE THING!
8). 嵌套判断
Preferred:
Not preferred:
一旦发现某个条件不符合, 立即返回, 条理更清晰
9). 参数过多
Preferred:
Not preferred:
当发现实现某一功能需要传递的参数太多时, 就预示着你应该聚合成一个model类了...这样代码更整洁, 也不容易因为参数太多导致出错。
10). 回调方法
Preferred:
函数调用的可知性, 回调时被调用者要知道其调用者, 方便信息的传递, 所以建议在回调方法中第一个参数中加上调用者。
Well, 不知不觉已经整理了10个了, 额, 太多了, 不知道童鞋们还有木有耐心看了, 好吧, 这一段就到此为止吧, 下面写一下block的编码规范, 各位看官, 预知后事如何, 且继续look look, 哈哈...
Block的循环引用问题
Block确实是个好东西, 但是用起来一定要注意循环引用的问题, 否则一不小心你就会发现, Oh My God, 我的dealloc肿木不走了...
如此在上面定义一个weakSelf, 然后在block体里面使用该weakSelf就可以避免循环引用的问题. 那么问题来了...是不是这样就完全木有问题了? 很不幸, 答案是NO, 还是有问题。问题是block体里面的self是weak的, 所以就有可能在某一个时段self已经被释放了, 这时block体里面再使用self那就是nil, 然后...然后就悲剧了...那么肿么办呢?
解决方法很简单, 就是在block体内define一个strong的self, 然后执行的时候判断下self是否还在, 如果在就继续执行下面的操作, 否则return或抛出异常.
什么情况下会出现block里面self循环引用的问题? 这个问题问的好, 哈哈...简单来说就是双边引用, 如果block是self类的property (此时self已经retain了block), 然后在block内又引用了self, 这个情况下就肯定会循环引用了...
由于不良的代码习惯和编程规范或多或少会引起一些意想不到的Bug,由此可见一个好的编码习惯是多么的重要!
以下是我将自己所认为的一些比较好的代码规范,跟大家分享一下。
命名规范
iOS命名有两大原则:可读性和防止命名冲突(通过加前缀来防止)。Objective-C 的命名通常都比较长,遵循驼峰式命名法。一个好的命名标准很简单,就是做到在开发者一看到名字时, 就能够懂得它的含义和使用方法。
1. 类的命名:每个模块都要加上自己的前缀,前缀在编程接口中非常重要,可以区分软件的功能范畴并防止不同文件或者类之间命名发生冲突。如:
相册模块(PhotoGallery)的代码都以PG作为前缀:PGAlbumViewController, PGDataManager。
2. 常量的命名:
对于常量的命名最好在前面加上字母k作为标记. 如:
1 | static const NSTimeInterval kAnimationDuration = 0.3; |
1 | NSString *const UIApplicationDidEnterBackgroundNotification |
II.全局常量(通知或者关键字等)尽量用const来定义. 因为如果使用宏定义, 一来宏可能被重定义. 二来引用不同的文件可能会导致宏的不同. P.S. 对于#define也添加一下前缀k(强迫症, 哈哈...)
3.变量和对象的命名
给一个对象命名时建议采用修饰+类型的方式. 如果只用修饰命名会引起歧义, 比如title (这个到底是个NSString还是UILabel?). 同样的, 如果只用类型来命名则会缺失作用信息, 比如label (好吧, 我知道你是个UILabel, 但是我不知道它是用来做什么的呀?). So, 正确的命名方式为:
1 2 | titleLabel //表示标题的label, 是UILabel类型 confirmButton //表示确认的button, 是UIButton类型 |
*)aString
如果某个命名已经很明确了, 为了简洁可以省去类型名. 比如scores, 很明显是个array了, 就不必命名成scoreArray了
编码规范
所谓写规范的代码就是写别人能看得懂的代码,有一句话是这么说的,“复杂的代码往往都是新手所写,只有经验老道的高手才能写出简单,富有表现力的代码”。
代码越少,Bug也越少,没有重复逻辑的代码更易于维护,当你修复了一个bug,如果相同的逻辑还出现在另外一个地方,而你没意识到,你有没有觉得很冤?
以下是一些优秀程序员总结的代码规范,受益匪浅~~~
1). 判断nil或者YES/NO
Preferred:
1 2 | if (someObject) { ... } if (!someObject) { ... } |
1 2 | if (someObject == YES) { ...} if (someObject != nil) { ...} |
2). 条件赋值
Preferred:
1 | result = object ? : [self createObject]; |
1 | result = object ? object : [self createObject]; |
3). 初始化方法
Preferred:
1 2 3 4 | NSArray *names = @[@ "Brian" , @ "Matt" , @ "Chris" , @ "Alex" , @ "Steve" ]; NSDictionary *productManagers = @{@ "iPhone" : @ "Kate" , @ "iPad" : @ "Kamal" }; NSNumber *shouldUseLiterals = @YES; NSNumber *buildingZIPCode = @10018; |
4). 定义属性
Preferred:
1 | @property (nonatomic, readwrite, copy) NSString *name; |
如果是内部使用的属性, 那么就定义成私有的属性(定义到.m的class extension里面)
对于拥有Mutable子类型的对象(e.g. NSString, NSArray, NSDictionary)一定要定义成copy属性. Why? 示例: NSArray的array = NSMutableArray的mArray; 如果mArray在某个地方改变了, 那array也会跟着改变. So, make sense?
尽量不要暴露mutable类型的对象在public interface, 建议在.h定义一个Inmutable类型的属性, 然后在.m的get函数里面返回一个内部定义的mutable变量. Why? For security as well!
5). BOOL赋值
Preferred:
1 | BOOL isAdult = age > 18; |
1 2 3 4 5 6 7 8 9 | BOOL isAdult; if (age > 18) { isAdult = YES; } else { isAdult = NO; } |
6) 拒绝死值
Preferred:
1 2 3 | if (car == Car.Nissan) or const int adultAge = 18; if (age > adultAge) { ... } |
1 2 3 | if (carName == "Nissan" ) or if (age > 18) { ... } |
7). 复杂的条件判断
Preferred:
1 2 3 4 5 6 7 8 9 10 11 | if ([self canDeleteJob:job]) { ... } - (BOOL)canDeleteJob:(Job *)job { BOOL invalidJobState = job.JobState == JobState.New || job.JobState == JobState.Submitted || job.JobState == JobState.Expired; BOOL invalidJob = job.JobTitle && job.JobTitle.length; return invalidJobState || invalidJob; } |
1 2 3 4 5 6 7 | if (job.JobState == JobState.New || job.JobState == JobState.Submitted || job.JobState == JobState.Expired || (job.JobTitle && job.JobTitle.length)) { //.... } |
8). 嵌套判断
Preferred:
1 2 3 4 5 | if (!user.UserName) return NO; if (!user.Password) return NO; if (!user.Email) return NO; return YES; |
1 2 3 4 5 6 7 8 9 10 11 12 | BOOL isValid = NO; if (user.UserName) { if (user.Password) { if (user.Email) isValid = YES; } } return isValid; |
9). 参数过多
Preferred:
1 2 3 4 5 6 | - (void)registerUser(User *user) { // to do... } |
1 2 3 4 5 6 | - (void)registerUserName:(NSString *)userName password:(NSString *)password email:(NSString *)email { // to do... } |
10). 回调方法
Preferred:
1 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; |
Well, 不知不觉已经整理了10个了, 额, 太多了, 不知道童鞋们还有木有耐心看了, 好吧, 这一段就到此为止吧, 下面写一下block的编码规范, 各位看官, 预知后事如何, 且继续look look, 哈哈...
Block的循环引用问题
Block确实是个好东西, 但是用起来一定要注意循环引用的问题, 否则一不小心你就会发现, Oh My God, 我的dealloc肿木不走了...
1 2 3 4 5 6 | __weak typeof (self) weakSelf = self; dispatch_block_t block = ^{ [weakSelf doSomething]; // weakSelf != nil // preemption, weakSelf turned nil [weakSelf doSomethingElse]; // weakSelf == nil }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | __weak typeof (self) weakSelf = self; myObj.myBlock = ^{ __strong typeof (self) strongSelf = weakSelf; if (strongSelf) { [strongSelf doSomething]; // strongSelf != nil // preemption, strongSelf still not nil [strongSelf doSomethingElse]; // strongSelf != nil } else { // Probably nothing... return ; } }; |
什么情况下会出现block里面self循环引用的问题? 这个问题问的好, 哈哈...简单来说就是双边引用, 如果block是self类的property (此时self已经retain了block), 然后在block内又引用了self, 这个情况下就肯定会循环引用了...
相关文章推荐
- 解决 iOS ViewController Push或者Pop 时的黑影
- IOS ARC项目使用非ARC文件 MRC项目使用ARC文件
- iOS7 新后台及下载SDK介绍
- iOS 各种正则表达式
- ios cell常用属性
- iOS_Block. 代理. NSUserDefaults.的简单实用
- VMware Workstation虚拟机Mac OS系统进入BIOS方法
- 一个很好用的发送iOS推送的mac应用
- IOS中获取各种文件的目录路径的方法
- iOS开发 关于iBeacon的一些记录
- IOS 字符串的拼接问题
- iOS 利用 framework 进行动态更新
- ios 深copy 浅copy
- iOS中书写代码规范34条小建议:
- iOS 直播推流SDK -- PLCameraStreamingKit
- IOS封装自定义布局的方法
- IOS8 设置TableView Separatorinset 分割线从边框顶端开始
- 高级iOS工程师的博客
- 截取点击事件
- git 命令总结