iOS 代码规范

2019-03-21  本文已影响0人  周小周_

基本原则:

清晰又简洁的代码当然是最好,但简洁不如清晰重要。不要使用单词的简写,除了非常常用的简写以外,尽量使用单词全称。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、 代码缩进

  1. 使用 xcode 默认缩进,即 tab = 4空格
  2. 使用 xcode 中 re-indent 功能定期对代码格式进行整理,步聚:点选要进行重构的文档, Control+A 全选该文档,然后选择XCODE -> Editor -> Structure -> Re-Indent 即可对代码进行重构 !
  3. 相同类型变量声明需要独行声明

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 -” 与下面的代码之前不要空行
遵循一般性的代码规范

参考:

iOS团队编程规范

【iOS】命名规范

iOS开发代码规范(通用)

上一篇下一篇

猜你喜欢

热点阅读