前言
需求是暂时的,变化才是永恒的,面向变化编程,而不是面向需求编程。
简洁的代码让bug无处藏身,要写出明显没有bug的代码,而不是没有明显bug的代码。
不要过分追求技巧,降低程序的可读性。
先把眼前的问题掉,解决好,再考虑将来的扩展问题。
作为一名合格的程序员,应该要明确一点:我们写的代码不只是给自己看的,优雅的编码风格以及良好的代码注释对我们日常的开发是十分必要的。当我们在编写代码时,要有一个意识:此刻有千千万万人正在看着自己写代码,如何让他们都能迅速看得懂自己写的代码,这就需要我们在平常开发的时候,时时刻刻都要进行代码规范。
一、命名
命名应该做到含义清楚、使用全称、除众所周知的缩写外不使用缩写,遵循驼峰命名法。
类名
类名使用大驼峰式命名:每个单词首字母都采用大写字母,带项目缩写前缀。例如:SHBaseViewController
。
ViewController的结尾必须以ViewController结束。例如:SHCourseDetailViewController
。
View的结尾必须以View结束。例如SHCourseDetailBottomView
。
Model的结尾必须以Model结束。例如SHHomeworkModel
。
TableViewCell的结尾以Cell结束,并要将所属模块一起表述清楚。例如:SHCourseDetailTeacherCell
。
TableView的HeaderView和FooterView以Header和Footer结束,并将所属模块一起表述清楚。例如SHCourseDetailTeacherHeader
。
属性名
属性名使用小驼峰式命名:第一个单词首字母小写,后面的单词首字母都以大写开始。例如:
属性要在property之后空一格;属性的每个关键字之间空一格;括号外面,先空一格再写声明属性;
属性关键字推荐按照内存管理、原子性、读写、setter、getter的顺序排列。关键字为assign时可以不写。
Block、NSString、NSNumber、NSArray、NSDictionary、NSSet都需要使用Copy关键字,但是NSMutableString、NSMutableArray、NSMutableDictionary、
NSMutableSet都不得使用Copy关键字。
NSArray、NSMutableArray、NSSet、NSMutableSet中的元素如果是相同类型,使用__kindof修饰。
例如:
@property (strong, nonatomic) SHBaseView *backgroundView;
@property (copy, nonatomic) NSString *teacherName;
@property (copy, nonatomic) NSArray<__kindof SHTeacherModel> *teacherModelArray;
常量名
对于仅限于当前文件的常量,以字符k开头,以static const做修饰。
例如:static NSString *const kIdentifyTeacher = @"teacher"
;
对于外部可见的常量,则以当前类的类名开头,在实现文件中定义其值,在头文件中进行extern声明。
宏定义
使用#define进行宏定义时,所有字母大写,中间以_分割。宏定义中的表达式和变量必须以小括号括起来。
尽量不要使用宏定义字符串,而应该使用常量来定义。
枚举
枚举类型的命名与类命名一致,都使用带项目前缀的大驼峰命名法。枚举中的枚举项以该枚举类型名称开头。
使用NS_ENUM定义通用枚举,使用NS_OPTIONS定义位移枚举。
例如:
typedef NS_ENUM(NSInteger, SHHomeworkItemType) {
SHHomeworkItemTypeImage = 1,
SHHomeworkItemTypeAudio = 2,
SHHomeworkItemTypeText = 3
};
方法
方法名采用小驼峰式命名;不能使用new、alloc作为起始单词;不能使用前缀。
不能使用and连接属性参数;如果方法描述两种独立行为,使用and连接。例如:
- (instancetype)initWithCourseId:(NSString *)courseId courseType:(NSString *)courseType;
表示对象行为的方法、执行性方法应该以动词开头,但不能以do或does开头,因为这些词毫无意义;也不能在动词前使用副词或形容词修饰;尽量不要使用other、old等泛指性单词。例如:
- (void)insertObject:(ObjectType)anObject atIndex:(NSUInteger)index;
有返回值的方法应该以返回的内容开头,但不能以get开头。例如:
- (ObjectType)objectAtIndex:(NSUInteger)index;
Init方法以最少参数为原则,其他init方法在此方法上扩展。
类别(Category)
类别命名需要带前缀,遵循大驼峰命名法。例如:
UIButton+SHEdgeInsets
协议
协议名使用Delegate做后缀。必须实现的协议方法用required修饰;可选实现的方法用optional修饰。
协议方法名使用did和will通知delegate已经发生和将要发生的变化。
协议方法中的第一个参数建议为当前对象。当只有当前对象一个参数时,方法名要符合实际含义;当协议方法存在两个以上参数的时候,以类名开头,表示此方法属于哪个类。例如:
@protocol SHCourseDetailBottomViewDelegate
- (void)askButtonActionInCourseBottomView:(SHCourseDetailBottomView *)bottomView;
- (void) courseBottomView:(SHCourseDetailBottomView *)bottomView didPressedButtonAtIndex:(NSInteger)index;
@end
协议的内存管理关键字要使用weak。
二、注释
优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在做什么,而不需要注释的辅助。但以下几种情况比较适合写注释:
- 公共接口,注释告诉使用接口的人当前类的作用及实现功能;
- 涉及专业层次的代码,注释表示出实现原理和思想;
- 容易产生歧义的代码,这种代码最好还是不要存在
对于注释的内容,相对于“做了什么”,更应该说明“为什么这么做”。
import注释
当import的文件较多时,需要对这些语句进行分组,并标记每组的类型。例如:
#import "SHHomeCourseDetailViewController.h"
#import
//view controller
#import "SHTagIntroViewController.h"
#import "SHRecommendViewController.h"
//section
#import "SHCourseDetailLectureSection.h"
#import "SHCourseDetailSubClazzSection.h"
#import "SHCourseDetailUnitCourseSection.h"
#import "SHCourseDetailActivitySection.h"
//cell
#import "SHCourseDetailCommonInfoCell.h"
#import "SHCourseDetailSubClazzCell.h"
#import "SHCourseDetailTagCell.h"
#import "SHCourseDetailUnitCourseCell.h"
#import "SHCourseDetailPracticeCell.h"
#import "SHHomeDetailLiveListCell.h"
#import "SHCourseDetailImageCell.h"
#import "SHHomeCourseWebCell.h"
#import "SHCourseDetailRewardCell.h"
#import "SHCourseDetailActivityPaidCell.h"
#import "SHCourseDetailActivityUnpaidCell.h"
#import "SHCourseDetailActivityEmptyCell.h"
#import "SHCourseDetailActivityIntroCell.h"
//view
#import "SHCourseDetailNavigationView.h"
#import "SHCourseDetailCountDownView.h"
#import "SHCourseDetailBottomView.h"
#import "SHPlayerCoverView.h"
//manager && Service
#import "SHHomeService.h"
#import "SHPayService.h"
#import "SHNewDownloadManager.h"
#import "SHDownLoadManager.h"
属性注释
较短的注释写在属性之后,用空格隔开;较长的注释写在属性上面。内容特别多的注释使用/* */。例如:
@property (strong, nonatomic) UIView *containerView; //this is a shor comment
//this is a long comment
@property (strong, nonatomic) SHExcellentShareCardView *cardView;
方法注释
公开或重要的方法、分类、协议都需要做注释。注释使用Xcode自带的快捷键(command+option+/)添加注释。
todo
未完成或需要后面修改的代码使用//TODO:来注释。例如:
//TODO:YangJian request authority
三、代码格式化
空格
使用4空格缩进,禁止使用tab字符。设置方法:command+逗号 > Text Editing > Identation。
指针*与类型之间有一个空格,与变量之间无空格。例如:NSString *teacherName
;
一元运算符与变量之间没有空格;而任何二元、三元运算符左右两边都有一个空格。
方法声明的-和+之后有一个空格;参数的类型和变量之间无空格;如果参数为对象,参数的类名和*之间有一个空格。例如:
- (void)askButtonActionInCourseBottomView:(SHCourseDetailBottomView *)bottomView;
左小括号和变量之间不得出现空格;同理右小括号和变量之间也不得出现空
if、for、while、switch、do等保留字与括号之间必须加空格。
换行与空行
方法与方法之间空且只空一行。
在方法开始时的左大括号必须独占一行;代码块中的左大括号建议换行;任何右大括号必须独占一行。
return语句之前建议加一个空行。
不要乱加空行,例如在右大括号之前就不要加空行了。
每行代码一般不超过80个字符,最多不超过100个。可以在Xcode中设置,command+逗号 > Text Editing > Page guide at column设置为100。
四、编码规范
所有代码块都应使用大括号包括起来,就算只有一行代码也要加上大括号。
if语句
if语句应尽量穷举所有情况,且每种情况都有明确处理。
尽量少嵌套,善于使用return提前返回错误,正确情况在最后处理。
推荐条件判断应该常量在左,变量在右。
条件表达式过长应单独抽出来,赋值给一个BOOL值。
不要在条件判断中执行复杂语句,将复杂逻辑判断赋值给一个BOOL值可提高代码阅读性。
switch语句
每个分支都要用大括号包起来,大括号要将break关键字一起括起来。
枚举类型不能有default值,除枚举类型外都必须有default分支。
循环语句
循环体中的语句要考虑性能问题,能移到循环体外的就要移到循环体外。
五、结构
项目结构
在遵循MVC架构的基础上扩展一些模块。
//TODO:因为项目调整中,此模块后续添加
代码结构
私有属性使用扩展声明。
属性都使用懒加载实现getter方法。
开放只读关键字的属性,在扩展中使用readwrite声明,在头文件中使用readonly声明。
头文件中少引用其他头文件,引用文件放到实现文件中,在头文件中使用到的类使用@class声明。
方法的顺序应按照以下顺序,类方法 > dealloc > init > 生命周期 > private > public > action > 代理 > getter;且每个模块之间使用#pragma mark分组。
尽量轻量级的视图控制器,ViewController只做view的内容更新和屏幕的交互等操作,较大的业务逻辑移到ViewModel中。
复杂的视图控制器使用子视图控制器。
类声明文件中在import之后添加NS_ASSUME_NONNULL_BEGIN,在@end之后添加NS_ASSUME_NONNULL_END;方法的返回值和参数如果允许为空,添加nullable修饰。
适当添加编译标识符。
过期的代码和文件及时清理。
不再维护或者将删除的代码使用DEPRECATED_MSG_ATTRIBUTE标识。
AppDelegate
只添加application相关方法,其他逻辑按功能进行分类。
六、其他
类应该单一、封闭,少使用单例。初始化的init方法只添加必要属性,非必要参数开放属性。
TableView使用重用机制,注册重用的方法是:
- (void)registerClass:(Class)cellClass forCellReuseIdentifier:(NSString *)identifier;
- (void)registerClass:(Class)aClass forHeaderFooterViewReuseIdentifier:(NSString *)identifier;
TableView的高度要做缓存。
不要对UITalbleView进行封装,事实证明自己封装UITableView就是一个大坑,新人来了以后很不适应。
一个方法的代码建议不超过100行。
属性在init、dealloc、setter、getter中使用下划线访问,其他地方使用self.访问。
block中要记得使用__weak和__strong打断循环引用。
NSTimer是会引起循环引用的,改用我们自己的BJTimer。
不要放太多文件到预编译头文件pch中,会使编译所读变慢。