iOS开发-Objective-C语言代码规范-OC代码规范集合-iOS代码规范

为什么要做代码规范?

目前的项目大多都是由一个团队来完成,如果没有统一的代码规范,那么每个人的代码必定会风格迥异,在工作中肯定会有多个人同时开发同一模块的情况,即使是分工十分明晰的,等到整合代码、CodeReView、工作接力等情况时问题就会显现出来。统一的风格使得代码可读性大大提高了,人们看到任何一段代码都不用去浪费更多的时间去琢磨。规范不是对开发的制约,而确实是有助于提高开发效率的。,规范的代码在团队的合作开发中是非常有益而且必要的。

代码规范能带来什么好处?

1.规范的代码可以促进团队合作

在文章开篇的时候就已经提及了:统一的风格使得代码可读性提高,在同事之间看到任何一段代码都不用去浪费更多的时间去琢磨,可以高效的完成开发工作。

2.规范的代码可以减少bug处理

有规范的对参数进行输入输出,有规范的异常处理,没规范的日志处理等等,bug不但可以有效减少,查找bug也变得轻而易举。

3.规范的代码可以降低维护成本

开发过程中的代码质量直接影响着维护的成本,可读性高的代码维护成本必然会大大降低。 而且,维护工作不仅仅是读懂原有代码,还需要在原有代码基础上作出修改,因此,统一的风格有利于长期的维护。

4.规范的代码有助于代码审查

代码审查可以及时纠正一些错误,对开发人员的代码规范作出监督。团队的代码审查同时也是一个很好的学习机会,对成员的进步也是很有益的,同时代码审查也有助于代码规范的实施。

5.养成代码规范的习惯,有助于自身的成长

有很多时候去看自己曾经写得代码是不是没有头绪呢?尤其是出现bug的时候需要逐行的debug?我们应该做的就是规范开发,减少自己出现的错误,规范开发最大的受益人其实是自己。


下面开始我们的正题:代码规范

《iOS技术部门代码规范1.0范本》

本文整合了谷歌、raywenderlich.com、58到家、NetStars(日)公司在开发过程中关于iOS端(Objective-C语言)的代码规范,适用于中小型团队(0-30人协作)。

核心原则 意义
原则一:代码应该简洁易懂,逻辑清晰 软件是需要人来维护的,不要过分追求技巧,降低程序的可读性。
原则二:面向变化编程,而不是面向需求编程 不能仅仅为了当前的需求,写出扩展性强,易修改的程序才是负责任的做法,对自己负责,对公司负责。
原则三:先保证程序的正确性,防止过度工程 过度工程(over-engineering):在正确可用的代码写出之前就过度地考虑扩展,重用的问题,使得工程过度复杂。

引用改编自 https://www.jianshu.com/p/d7e87107073c

目录:

  • 注释的写法
  • 命名与规范
  • 分类的方法规范
  • 代码组织结构规范
  • 条件语句规范
  • 补充规范

注释的写法

首先我们就从大家最关心的“代码注释”作为切入,虽然写起来很痛苦,但注释是保证代码可读性的关键。下面的规则给出了你应该什么时候、在哪进行注释。注释很重要,好的代码应该能自成文档。与其给类型及变量起一个晦涩难懂的名字,再为它写注释,不如直接起一个有意义的名字。当你写注释的时候,记得你是在给你的听众写,即下一个需要阅读你所写代码的贡献者。大方一点,下一个读代码的人可能就是你?

1.Class 类注释

每个接口、类别以及协议应辅以注释,以描述它的目的及与整个项目的关系。如下:

/**
 委托代理,用来处理关于app的启动和关闭过程中的xxx、xxx事件,被app的xxx控制器拥有。
*/ 
@interface MyAppDelegate : NSObject {
  ...
}
@end
2.Property 属性注释
/**
 标识是起始地还是目的地
 */
@property (nonatomic, copy) NSString *userName;

/**
 地址信息模型
 */
@property (nonatomic, copy) UserInfoModel *userInfoModel;
3.Method 方法声明注释
/**
 登录倒计时文本
 @param timerLabel 倒计时Label对象
 @param timeText   倒计时文本数据
*/
- (void)timerLabel:(UILabel *)timerLabel timeText:(NSString *)timeText;
4.Block 代码块注释
/**
 验证搜索成功失败模块
 @param searchResponse 搜索返回结果
 @param error 错误信息
 @return 如果返回 YES 代表搜索成功, 返回 NO 代表搜索失败
*/
typedef BOOL(^SearchResultBlock)(AMapPOISearchResponse *searchResponse, NSError *error);
5.NSEnum 枚举注释
/**
(参考YYText)
YYTextVerticalAlignmentTop: 顶部对齐
YYTextVerticalAlignmentCenter: 居中对齐
YYTextVerticalAlignmentBottom: 底部对齐
*/
typedef NS_ENUM(NSInteger, YYTextVerticalAlignment) {
    YYTextVerticalAlignmentTop =    0, ///< Top alignment.
    YYTextVerticalAlignmentCenter = 1, ///< Center alignment.
    YYTextVerticalAlignmentBottom = 2, ///< Bottom alignment.
};
6.局部变量注释
@interface SomeViewController () {
    SomeModel * someModel; // 用来存储信息
    NSString * someStr;    // 用来检测的字段
    UIView * someView;     // 用来显示信息的视图
}
7.Method 方法实现注释①
/** 地址选择确认返回上一页 */
- (void)doSomeThing:(id)someObject{
    /**
     判断内容和结果
    */ 
    if (条件判断1) {
        if (内部条件) {
            do...
        }else{
            do...
        }
    }
    
    /**
     判断内容和结果
    */ 
    if (条件判断2) {
        do...
        return;
    }
    
    /**
     [回调数据][目标接受页面]
     tips:当block嵌套太多的时候,对于读代码的人来说非常困惑,因为往往可能追了3,4个block进去之后,被以下这点代码传出去了,所以,在使用block的时候,花点时间注明回调的数据以及目标页面方便自己也方便别人
    */ 
    _someBlock(xxx);
}
8.Method 方法实现注释②
// tips:对于一个不常用的第三方、底层类库等代码,可以简单的把每一个过程都写一下,这样的话易读性很强
- (void)createCAEmitterLayer{
    // 1.创建CAEmitterLayer
    CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
    
    // 2.设置CAEmitterLayer的属性(最主要的是前四个)
    // 发射源的形状 是枚举类型
    emitterLayer.emitterShape = kCAEmitterLayerLine;
    
    // 发射模式 枚举类型
    emitterLayer.emitterMode = kCAEmitterLayerSurface;
    
    // 发射源的size 决定了发射源的大小,如果做了倾斜或者便宜屏幕宽度是不够的,那时候就需要自定义
    emitterLayer.emitterSize = self.view.frame.size;
    
    // 发射源的位置
    emitterLayer.emitterPosition = CGPointMake(self.view.bounds.size.width * 0.5, -10);
    
    // 渲染模式 枚举类型 (⭐️这个渲染模式表达效果会很不好,不常用,kCAEmitterLayerAdditive,可以让重叠的部分高亮)
    emitterLayer.renderMode = kCAEmitterLayerAdditive;
    
    // 3.添加到目标视图的layer上
    [self.view.layer addSublayer:emitterLayer];
}
9.Method 方法实现注释③
/** 
 登录请求验证
 @param userId 用户名
 @param password 密码
 @param complete 执行完毕的block
*/
- (void)loginRequestWithUserId:(NSString *)userId password:(NSString *)password complete:(void (^)(CheckLogon *result))complete{
	
}

命名的规范

1.通用变量命名

保持使用驼峰命名法,建议的写法如下:

@property (nonatomic, copy) NSArray *childPaths;

反例:

@property (nonatomic, copy) NSArray *childpaths;
2.宏命名

不带参数情况下,全部大写,单词间用 _ 分隔,建议写法如下:

#define THIS_IS_A_TICKET @"THIS_IS_A_TICKET"

不带参数情况下,避免和类自己的参数冲突可以以【小写】字母k或者自定义字母作为开头,后面遵循驼峰命名,建议写法如下:

#define kScreenWidth [UIScreen mainScreen].bounds.size.width

带参数的情况下,遵循驼峰命名,建议写法如下:

#define getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]

反例:

#define homepageadbannerheight 85
3.控件命名

控件的命名需要注意突出后缀,建议写法如下:

UIView *userInfoView;

反例:

UIView *userInfo;

关键是让人通过命名就能知道其目的或者用法,一般控件的命名都是接上所生命类的后缀,示例情况如下:

UI控件名 自定义名
UIView someView
UViewController someViewController
UIButton someButton
UILabel someLabel
UITableView someTableView
4.类的命名①

一般在自定义类的时候需要注意命名规范,首字母大写每个单词首字母大写(大驼峰命名法),建议写法如下:

@interface UserInfoManager : NSObject
5.类的命名②

一般在不同业务线上会有不同分支的首页、子页面,很多时候会有重复的信息,比如两个模块都有HomePage,这时候就需要在前面加上前缀,建议写法如下:

/**
 活动页面
*/
@interface ActivityHomeViewController : UIViewController

反例:

@interface HomeViewController : UIViewController

分类的方法规范

我们在开发过程中为了更好的使用系统方法,一般会使用分类来添加方法,来满足开发需要,分类的作用就是在不修改原有类的基础上,为一个类扩展方法,最主要的是可以给系统类扩展我们自己定义的方法。为了避免和系统方法或者别的同事所做的分类方法的冲突,一般都会在方法面前添加小写字母来做区别,建议写法如下:

@interface UIView (TouchBlock)
- (void)tb_touchView:(void (^)(void))block;
@end

反例:

@interface UIView (TouchBlock)
- (void)touchView:(void (^)(void))block;
@end

代码组织结构规范

1. #import 文件的顺序

在开发过程中,使用#import来导入头文件是必不可少的工序,但有的功能或者业务模块需要导入的文件就会非常多,看着会很别扭,所以指定一套方案来改善这一现状是很有必要的,建议顺序如下:

// 系统库
#import 
#import 

// 第三方库
#import 

// 自定义类
#import "MyButton.h" 
#import "UserInfoModel.h"
2.控制器中方法分区

一般在控制器中,会有控制器生命周期相关方法以及不同的业务代码,可以使用#pragma mark来进行简单的方法分区,建议写法如图:

可以稍做优化,区分为几个模块,示例如下:

#pragma mark - Life Cycle 生命周期
#pragma mark - Request 网络请求
#pragma mark - Delegate 代理实现
#pragma mark - Event Response 事件响应
#pragma mark - Methods 方法

根据每个人的喜好不一致可以按照自己的想法来写,这样在别人梳理文件的时候思路会比较清晰


条件语句规范

1.{}括号的规范

一般会出现以下几种情况:

// 1
if (YES) {
    Do();
}

// 2
if (YES) DO();

// 3
if (YES)
   Do();
   
// 4
if (YES) 
{
   Do(); 
}

以上第一种方式是苹果主推的,经过调研,方法2、3、4虽然在写法上会有所简化,但在风格上看来还是有所欠缺,所以更多的建议使用第一种。

2.减少逻辑嵌套

一般在判断的时候可以使用最简单的方法来做最简单的事情,使用return可以减少复杂度,提高代码可读性,建议写法如下:

- (void)someMethod {
    if(![someThing]) {
        return;
    }
    // continue do something 
}

反例:

- (void)someMethod {
    if([someThing]) {
        // continue do something
    }else{
    	return;
    }
}
3.复杂表达式

常用写法:

if ([self getUserInfoModel] == nil && ![self userLogin]){
	[self showLoginVC];
}

一般会有很长的表达式或者多个待判断的值,在这种情况下,建议以下写法:

BOOL userInfoModelIsNil  = [self getUserInfoModel] == nil;
BOOL userIsLogin = ![self userLogin];
BOOL showLoginVC = userModelIsNil && userIsLogin;
if (showLoginVC) {
    [self showLoginVC];
}

第一种写法大家都是很常用的,表达很简洁,但是从阅读代码和调试代码的角度看,推荐第二种,因为每个条件和句子的意义很明显的就能看出来。


补充规范

1.如果一行有非常多的参数,更好的方式是将每个参数单独拆成一行。如果使用多行,将每个参数前的冒号对齐,建议写法如下:

- (void)doSomethingWithUserName:(NSString *)name
                        address:(NSString *)address
                     doorNumber:(float)number {
  ...
}

当第一个关键字比其它的短时,保证下一行至少有 4 个空格的缩进。这样可以使关键字垂直对齐,而不是使用冒号对齐:

- (void)getA:(AClass *)A
    longKeywordB:(BClass *)B
    evenLongerKeywordC:(CClass *)C {
  ...
}

2.常量是容易重复被使用和无需通过查找和代替就能快速修改值。常量应该使用static来声明而不是使用#define,除非显式地使用宏。建议写法如下:

static NSString * const XXXAboutViewControllerCompanyName = @"XXXCompanyName";
static CGFloat const XXXImageThumbnailHeight = 50.0;

反例:

#define CompanyName @"XXXCompanyName"
#define ImagethumbnailHeight 50

3.布尔值的判断书写,Objective-C使用YES和NO。因为true和false应该只在CoreFoundation,C或C++代码使用。既然nil解析成NO,所以没有必要在条件语句比较。不要拿某样东西直接与YES比较,因为YES被定义为1和一个BOOL能被设置为8位。这是为了在不同文件保持一致性和在视觉上更加简洁而考虑。建议书写方式如下:

if (someObject) {

}
if (![anotherObject boolValue]) {

}

反例:

if (someObject == nil) {

}
if ([anotherObject boolValue] == NO) {

}
if (isAwesome == YES) {

} 
if (isAwesome == true) {

}

4.三元操作符的表达,当需要提高代码的清晰性和简洁性时,三元操作符?:才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。建议写法如下:

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

反例:

result = a > b ? x = c > d ? c : d : y;

5.在使用 Init方法 和 类构造方法 时需要注意:
Init方法应该遵循Apple生成代码模板的命名规则。返回类型应该使用instancetype而不是id

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

当类构造方法被使用时,它应该返回类型是instancetype 而不是id。这样确保编译器正确地推断结果类型。

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end

你可能感兴趣的:(Objective-C,iOS开发基础)