iOS 代码规范
基本原则:
清晰又简洁的代码当然是最好,但简洁不如清晰重要。不要使用单词的简写,除了非常常用的简写以外,尽量使用单词全称。API的名称不要有歧义,一看API就知道是以什么方式做了什么事情。
1、命名
类的命名:
大驼峰式命名:即每个单词的首字母采用大些字母。
使用能够反映类功能的名词短语。
DoneCourseListView
分类(类别)命名:
与类命名相同,此外需添加要扩展的类名和“+”
NSString+MTValidate
方法命名:
小驼峰式命名:首字母小写,之后每个单词首字母都大写
方法名使用动词短语。
- (void)setPostValue:(int)value
方法参数命名:
首字母小写,之后每个单词首字母都大写
具有足够的说明性,不需要添加类型前缀
- (void)sendUserInfo:(NSDictionary*)userInfo
宏命名:
全部大写,单词间用 _ 分隔。[不带参数]
#define THIS_IS_AN_MACRO @"THIS_IS_AN_MACRO"
以字母 k 开头,后面遵循大驼峰命名。[不带参数]
#define kWidth self.frame.size.width
小驼峰命名。[带参数]
#define getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]
枚举类型命名:
Enum类型的命名与类的命名规则一致
Enum中枚举内容的命名需要以该Enum类型名称开头
NS_ENUM定义通用枚举,NS_OPTIONS定义位移枚举
typedef NS_ENUM(NSInteger,UIViewAnimationTransition) {
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown
};
typedef NS_OPTIONS(NSUInteger,UIControlState) {
UIControlStateNormal=0,
UIControlStateHighlighted=1<<0,
UIControlStateDisabled=1<<1
};
分组命名:
使用英文,首字母大写,之后每个单词首字母都大写
每个分组使用模块的名字
使用的开源库统一放在“Library”分组下
使用的公共组件统一放在“Common”分组下
视图控制器及AppDelegate统一放在“Controllers”分组下
后缀要求:
视图控制器的子类应该以“ViewController”或者“Controller”做后缀
CourseViewController
试图的子类应该以“View”做后缀
CourseView
协议(委托)使用Delegate或者DataSource作为后缀
VideoPlayerDelegate
按钮的子类应添加后缀“Button”,UI控件以此类推
LoginButton
2、注释
优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在干什么,而不需要注释的辅助,如果做不到命名尽量的见名知意的话,就可以适当的添加一些注释或者mark。
但是以下三种情况比较适合写注释:
1、公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)。
2、涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。
3、容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。
除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。对于注释的内容,相对于“做了什么”,更应该说明“为什么这么做”。
注释示例:
1、属性注释
/// 学生
@property (nonatomic, strong) Student *student;
2、类注释
/** 类信息。此注释用在类声明的开头。
@TestClass
@这是一个测试类
*/
@interfaceTestClass :UIView
@end
3、方法命名注释
/**
根据请求的 URL 与 parameters 同步取出缓存数据
@param URL 请求的URL
@param parameters 请求的参数
@return 缓存的服务器数据
*/
+ (id)httpCacheForURL:(NSString *)URL parameters:(id)parameters;
4、import注释
如果有一个以上的import语句,就对这些语句进行分组,每个分组的注释是可选的。
// Frameworks
#import<Foundation/Foundation.h>;
// Models
#import "UserInfoModel.h"
// Views
#import "CourseCountView"
#import "MineHeaderView.h"
5、代码块注释
单行的用//+空格开头,多行的采用/* */注释
// [self.allCourseV dataWithTitle:@"购买课时" count:[NSString stringWithFormat:@"%lu", (unsigned long)allCount] desc:@""];
6、TODO
使用//TODO:说明 标记一些未完成的或完成的不尽如人意的地方
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//TODO:增加初始化
return YES;
}
3、格式化代码
1、 指针 "*" 位置
定义一个对象时,指针 "*" 靠近变量
NSString *userName;
2、 方法的声明和定义
在 - 、+ 和 返回值 之间留一个空格,方法名和第一个参数之间不留空格
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
3、 代码缩进
- 使用 xcode 默认缩进,即 tab = 4空格
- 使用 xcode 中 re-indent 功能定期对代码格式进行整理,步聚:点选要进行重构的文档, Control+A 全选该文档,然后选择XCODE -> Editor -> Structure -> Re-Indent 即可对代码进行重构 !
- 相同类型变量声明需要独行声明
4、对方法进行分组
使用 #pragma mark -方式对类的方法进行分组
方法与方法之间空一行
#pragma mark - private methods
- (**void**)samplePrivateMethod
{...}
- (**void**)sampleForIf
{...}
5、 大括号写法
对于类的method: 左括号另起一行写(遵循苹果官方文档)
对于其他使用场景(if,for,while,switch等): 左括号跟在第一行后边
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
任何需要写大括号的部分,不得省略
//错误示例
- (void)wrongExamples
{
BOOL someCondition = YES;
if (someCondition)
NSLog(@"this is wrong!!!");
while (someCondition)
NSLog(@"this is wrong!!!");
}
4、编码规范
1、if语句
①、须列出所有分支(穷举所有的情况),而且每个分支都须给出明确的结果。
推荐
var hintStr;
if (count <3) {
hintStr ="Good";
} else {
hintStr ="";
}
不推荐
var hintStr;
if (count <3) {
hintStr ="Good";
}
②、不要使用过多的分支,要善于使用return来提前返回错误的情况,把最正确的情况放到最后返回。
推荐
if (!user.UserName) return NO;
if (!user.Password) return NO;
if (!user.Email) return NO;
return YES;
不推荐
BOOL isValid = NO;
if (user.UserName)
{
if (user.Password)
{
if (user.Email) isValid = YES;
}
}
return isValid;
③、条件过多,过长的时候应该换行(1)。条件表达式如果很长,则需要将他们提取出来赋给一个BOOL值(2),或者抽取出一个方法(3)
(1)
if(condition1 &&
condition2 &&
condition3 &&
condition4) {
// Do something
}
(2)
BOOL finalCondition = condition1 && condition2 && condition3 && condition4
if (finalCondition) {
// Do something
}
(3)
if ([self canDelete]){
// Do something
}
- (BOOL)canDelete
{
BOOL finalCondition1 = condition1 && condition2
BOOL finalCondition2 = condition3 && condition4
return condition1 && condition2;
}
不推荐
if (condition1 && condition2 && condition3 && condition4) {
// Do something
}
④、条件语句的判断应该是变量在右,常量在左。
if (object == nil)容易误写成赋值语句, if (!object)写法很简洁
推荐
if (6== count) {
}
if (nil == object) {
}
if (!object) {
}
不推荐
if(count == 6) {
}
if(object == nil) {
}
⑤、每个分支的实现代码都须被大括号包围
推荐:
if (!error) {
return success;
}
也可以:
if (!error) return success;
不推荐
if (!error)
return success;
2、for语句
①、不可在for循环内修改循环变量,防止for循环失去控制。
②、避免使用continue和break。
- continue和break所描述的是“什么时候不做什么”,所以为了读懂二者所在的代码,我们需要在头脑里将他们取反。
- 最好不要让这两个东西出现,因为我们的代码只要体现出“什么时候做什么”就好了,而且通过适当的方法,是可以将这两个东西消灭掉的:
- 如果出现了continue,只需要把continue的条件取反即可
var filteredProducts = Array()
for level in products {
if level.hasPrefix("bad") {
continue;
}
filteredProducts.append(level)
}
我们可以看到,通过判断字符串里是否含有“bad”这个prefix来过滤掉一些值。其实我们是可以通过取反,来避免使用continue的:
for level in products {
if !level.hasPrefix("bad") {
filteredProducts.append(level)
}
}
消除 while 里的 break:将 break 的条件取反,并合并到主循环里
在 while 里的 break 其实就相当于“不存在”,既然是不存在的东西就完全可以在最开始的条件语句中将其排除。
while 里的 break:
while (condition1) {
...
if (condition2) {
break;
}
}
取反并合并到主条件:
while (condition1 && !condition2) {
...
}
在有返回值的方法里消除break:将break转换为return立即返回
//在有返回值的方法里break之后,再返回某个值。其实完全可以在break的那一行直接返回。
func hasBadProductIn(products: Array<String>) -> Bool {
var result = false
for level in products {
if level.hasPrefix("bad") {
result = true
break
}
}
return result
}
//这样写的话不用特意声明一个变量来特意保存需要返回的值,看起来非常简洁,可读性高。
func hasBadProductIn(products: Array<String>) -> Bool {
for level in products {
if level.hasPrefix("bad") {
return true
}
}
return false
}
3、Switch语句
①、每个分支都必须用大括号括起来
switch (integer) {
case 1: {
// ...
break;
}
case 2: {
// ...
break;
}
default: {
// ...
break;
}
}
②、使用枚举类型时,不能有default分支, 除了使用枚举类型以外,都必须有default分支,在Switch语句使用枚举类型的时候,如果使用了default分支,在将来就无法通过编译器来检查新增的枚举类型了。
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;
switch (menuType) {
case RWTLeftMenuTopItemMain: {
// ...
break;
}
case RWTLeftMenuTopItemShows: {
// ...
break;
}
case RWTLeftMenuTopItemSchedule: {
// ...
break;
}
}
4、函数
①、一个函数只做一件事(单一原则)
②、对于有返回值的函数(方法),每一个分支都必须有返回值
③、对输入参数的正确性和有效性进行检查,参数错误立即返回
④、如果在不同的函数内部有相同的功能,应该把相同的功能抽取出来单独作为另一个函数
⑤、将函数内部比较复杂的逻辑提取出来作为单独的函数
5、团队规范
说明:一个好的团队,理所当然有其严格的代码规范,好的代码不仅可以提高团队的开放效率,也更利于团队项目的后期维护,统一的代码风格,也是团队的核心,所以规范代码很有必要!
1、删除多余的空行 所有方法与方法之间空1行 所有代码块之间空1行
2、删除多余的注释
删除注释掉的代码
删除没有意义的注释
3、删除多余的方法
如果方法没有使用到,请删除它
如果方法没有执行任何业务逻辑,请删除它或者给出一定注释
4、删除未被使用的资源文件
5、添加必要的注释
所有.h 文件中的property 需要给出注释
所有自定义的方法需要给出注释
比较大的代码块需要给出注释
所有代码中出现的阿拉伯数字需要给出注释
程序中出现加密/解密 逻辑的操作地方,需要给出注释说明过程(无论是系统还是自定义)
6、整体代码风格需要统一
代码后面的”{“ 不需要单独占用一行
逻辑运算符 与“|” 代码之前空一格
“#pragmamark -” 与下面的代码之前不要空行
遵循一般性的代码规范
参考: