代码规范

2018-11-16  本文已影响0人  豆豆哥哥

OC篇

一. 核心原则

原则一:代码应该简洁易懂,逻辑清晰

因为软件是需要人来维护的。这个人在未来很可能不是你。所以首先是为人编写程序,其次才是计算机:

原则二:面向变化编程,而不是面向需求编程。

需求是暂时的,只有变化才是永恒的。

本次迭代不能仅仅为了当前的需求,写出扩展性强,易修改的程序才是负责任的做法,对自己负责,对公司负责。

原则三:先保证程序的正确性,防止过度工程

过度工程(over-engineering):在正确可用的代码写出之前就过度地考虑扩展,重用的问题,使得工程过度复杂。


引用《王垠:编程的智慧》里的话:

先把眼前的问题解决掉,解决好,再考虑将来的扩展问题。

先写出可用的代码,反复推敲,再考虑是否需要重用的问题。

先写出可用,简单,明显没有bug的代码,再考虑测试的问题。


为解决程序员命名歧义。统一Model命名:入参:XxxParam;出参:XxxVO

1.每一行的字数限制

80个字数限制
xcode设置 屏幕左上角Xode->Perference->Text Editing->"Page guide at column: 80" 打上勾
每一行的代码尽量不要超出80个字的长度,超出的回车排版, 方法名的冒号对齐

2.命名规范

首字母大写,每个单词首字母大写(大驼峰命名法)

尽量使用能够反映类功能的名词短语

eg:UserManage ,UserData等

UIKit里的UI界面部分

控件类型直接使用尾端的驼峰单词

eg:
UIView -> xxxView
UViewController -> xxxViewController
UIButton -> xxxButton
UILabel -> xxxLabel
UIImageView -> xxxImageView
UITableView -> xxxTableView
UITableViewCell -> xxxCell
UIAlertView -> xxxAlertView
UIScrollView -> xxxScrollView

类名过长,取中间的单词作为尾端

eg:
UIActivityIndicatorView -> xxxActivity
UIPickerView -> xxxPicker
UIProgressView -> xxxPorgress

分类(类别)命名

与类命名相同,此外需添加要扩展的类名"+"

eg:
NSString+Login UIView+XIB 等

协议(委托)命名

与类命名相同,使用Delegate或者DataSource作为后缀

eg:
UserManager 对应 UserManagerDelegate

方法名使用动词短语

如果该方式对内部使用的在前面加 "_"
eg: - (void) _loadData{}

如果该方法对外使用不需要加“_”
eg: - (void)viewDidLoad{}

首字母小写,之后每个单词首字母都大写

具有足够的说明性
成员变量不需要添加“_”前缀
成员变量添加“_”前缀

eg:NSMutableDictionary *_dataDic;

格式化代码
 1> 指针
  定义一个对象时,指针 "*" 靠近变量
   例子: NSString *userName;
 2> 方法的声明和定义
  在 - 、+ 和 返回值 之间留一个空格,方法名和第一个参数之间不留空格

可变数组或字典变量名

NSMutableDictionary *dataDicM 后加‘M’表示可变 ,必须写
NSDictionary *dataDicN 后加‘N’表示不可变, “N”可不写

常量(预定义,局部常量等)使用小写k开头的驼峰法

eg:#define kUpdateUserInfoNote @"kUpdateUserInfoNote"
可变用strong 不可变用copy修饰

宏 3种情况

全部大写,单词间用 _ 分隔。[不带参数]
例子: #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]]

枚举的内容使用 名称_小内容 加上注释

eg:typedef NS_ENUM(NSInteger, Code) { Code_Success = 200, //成功 Code_Fail = 201, //失败}

eg:typedef NS_ENUM(NSInteger, Code) {
Code_Success = 200, //成功 
Code_Fail = 201, //失败
}

IBAction 方法

如果是Button时间,方法名后缀使用Action,如果是内部使用,前面添加'_'

使用英文,全部小写,单词中间"_"隔开
添加UI模块名作为前缀,避免冲突
图片存放在 Images.xcassets

eg:bar_title icon_coin
注不经常变动的放在 Images.xcassets
经常变动的放在 ChangeImages.xcassets

优秀的代码大部分是可以自描述的,我们完全可以用程代码本身来表达它到底在干什么,而不需要注释的辅助。

但并不是说一定不能写注释,有以下三种情况比较适合写注释:

公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)。

涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。

容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。

除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。

最后,对于注释的内容,相对于“做了什么”,更应该说明“为什么这么做”。


在方法内部注释的地方使用 //即可
在方法上或属性注销的要用文档注释 com+sh+.
文件下的方法区域分类,使用#pragma mark -,可以把在文件路径下的方法分类并标记
eg:#pragma mark - Http

/// 学生
@property (nonatomic, strong) Student *student;
/** 
* @brief 登录验证
*
* @param personId 用户名
* @param password 密码
* @param complete 执行完毕的block
*
* @return
*/
+ (void)loginWithPersonId:(NSString *)personId password:(NSString *)password complete:(void (^)(CheckLogon *result))complete{
 //注释
if(I < 1){

}
}

避免相同的代码段在多个地方出现相同的代码,必须归纳出来并且用一个类封装起来
语句嵌套层次不得超过3层,超出的必须抽取出中间函数
g:for,while循环 if,do等
及时删除或注释掉无用的代码
确定不使用的代码应该删除

import "xxxx.h" 部分头文件时候,如果只是内部使用,请放在.m文件下,.h使用@class xxxx 声明该类即可
文件下有声明代理,把@protocol代理放在@interface 上面
代理方法必须包含该类名

eg:
@class ImageOperation;
@protocol ImageOperationDelegate<NSObject>
- (void) operation:(ImageOperation *)op didLoadImage:(UIImage *)image;
@end
@interface ImageOperation : NSOperation
@end

@implementation ViewController 上方使用 @interfaceViewController()

@end

使用部分代理,并且代理不被外部使用,请把他们都放在.m的interface ()下

eg:@interface ViewController () <UITableViewDataSource,UITableViewDelegate>

3.系统函数放上面,自定义函数放下面

如常见的viewcontroller

- (void)viewDidLoad{}
- (void)viewDidAppear:(BOOL)animated{}

然后跟着的是Delegate,DataSource
//如UICollectionViewDelegate,UICollectionViewDataSource
- (NSInteger)collectionView:(UICollectionView *)view numberOfItemsInSection:(NSInteger)section{}
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{}
......

最后跟自己的方法
- (void) xxxXxxx{}
中间使用#pragma mark-分割,如 Delegate,DataSource 和 自己的的方法

自己的方法

中间使用#pragma mark - 分割 不同的功能

中间使用#pragma mark - 网络请求

中间使用#pragma mark - btnAction

中间使用#pragma mark - NSNotification

中间使用#pragma mark - delegate

中间使用#pragma mark - 设置UI  一定要放在最下方

#pragma mark - Life Cycle Methods
- (instancetype)init
- (void)dealloc
 
- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated
 
#pragma mark - Override Methods
 
#pragma mark - Intial Methods
 
#pragma mark - Network Methods
 
#pragma mark - Target Methods
 
#pragma mark - Public Methods
 
#pragma mark - Private Methods
 
#pragma mark - UITableViewDataSource  
#pragma mark - UITableViewDelegate  
 
#pragma mark - Lazy Loads
 
#pragma mark - NSCopying  
 
#pragma mark - NSObject  Methods

符号篇

1大括号

控制语句(if,for,while,switch)中,大括号开始与行尾
函数中,大括号要开始于行首

2运算符

2.1 运算符与变量之间的间隔
2.1.1一元运算符与变量之间没有空格:
!bValue
~iValue
++iCount
*strSource
&fSum
2.1.2二元运算符与变量之间必须有空格
fWidth = 5 + 5;
fLength = fWidth * 2;
fHeight = fWidth + fLength;
for(int i = 0; i < 10; i++)

2.2多个不同的运算符同时存在时应该使用括号来明确优先级

在多个不同的运算符同时存在的时候应该合理使用括号,不要盲目依赖操作符优先级。

2 << 2 + 1 * 3 - 4

2 << (2 + 1 * 3 - 4)

3.if语句


-(id)initWithDictionary:(NSDictionary*)dict error:(NSError)err
{
   //方法1. 参数为nil
   if (!dict) {
     if (err) *err = [JSONModelError errorInputIsNil];
     return nil;
    }
 
    //方法2. 参数不是nil,但也不是字典
    if (![dict isKindOfClass:[NSDictionary class]]) {
        if (err) *err = [JSONModelError errorInvalidDataWithMessage:@"Attempt to initialize JSONModel object using initWithDictionary:error: but the dictionary parameter was not an 'NSDictionary'."];
        return nil;
    }
 
    //方法3. 初始化
    self = [self init];
    if (!self) {
        //初始化失败
        if (err) *err = [JSONModelError errorModelIsInvalid];
        return nil;
    }
 
    //方法4. 检查用户定义的模型里的属性集合是否大于传入的字典里的key集合(如果大于,则返回NO)
    if (![self __doesDictionary:dict matchModelWithKeyMapper:self.__keyMapper error:err]) {
        return nil;
    }
 
    //方法5. 核心方法:字典的key与模型的属性的映射
    if (![self __importDictionary:dict withKeyMapper:self.__keyMapper validation:YES error:err]) {
        return nil;
    }
 
    //方法6. 可以重写[self validate:err]方法并返回NO,让用户自定义错误并阻拦model的返回
    if (![self validate:err]) {
        return nil;
    }
 
    //方法7. 终于通过了!成功返回model
    return self;
}

首先判断出各种错误的情况然后提前返回,把最正确的情况放到最后返回。

4.for语句

5.Switch语句

方法函数

1. 一个函数的长度必须限制在50行以内

通常来说,在阅读一个函数的时候,如果视需要跨过很长的垂直距离会非常影响代码的阅读体验。如果需要来回滚动眼球或代码才能看全一个方法,就会很影响思维的连贯性,对阅读代码的速度造成比较大的影响。最好的情况是在不滚动眼球或代码的情况下一眼就能将该方法的全部代码映入眼帘。

2.一个函数只做一件事(单一原则)

每个函数的职责都应该划分的很明确(就像类一样)。

3对输入参数的正确性和有效性进行检查,参数错误立即返回

4如果在不同的函数内部有相同的功能,应该把相同的功能抽取出来单独作为另一个函数

5将函数内部比较复杂的逻辑提取出来作为单独的函数

6避免使用全局变量,类成员(class member)来传递信息,尽量使用局部变量和参数。

CGRect函数

面向协议编程

单例

*单例不能作为容器对象来使用

+ (instancetype)sharedInstance 
{ 
 static id sharedInstance = nil; 
 static dispatch_once_t onceToken = 0;
       dispatch_once(&onceToken, ^{ 
  sharedInstance = [[self alloc] init];
  }); 
 return sharedInstance; 
}

NSNotification

其他

对于某些暂时不用,以后可能用到的临时变量,为了避免警告,我们可以使用如下方法将这个警告消除:
- (NSInteger)giveMeFive 
{ 
 NSString *foo; 
 #pragma unused (foo) 
 return 5; 
}
#error Whoa, buddy, you need to check for zero here!
#warning Dude, don't compare floating point numbers like this! 
上一篇 下一篇

猜你喜欢

热点阅读