含义清楚,尽量做到不需要注释也能了解其作用,若做不到,就加注释,使用全称,不使用缩写。
大驼峰式命名:每个单词的首字母都采用大写字母
MFHomePageViewController
私有变量放在 .m 文件中声明
-
小驼峰式命名:第一个单词以小写字母开始,后面的单词的首字母全部大写
-
属性的关键字推荐按照 原子性,读写,内存管理的顺序排列。
-
Block、NSString属性应该使用copy关键字
typedef void (^ErrorCodeBlock) (id errorCode, NSString *msg);
// 注释
@property (nonatomic, strong) UIView *headerView;
// 将block拷贝到堆中
@property (nonatomic, copy) ErrorCodeBlock errorBlock;
@property (nonatomic, copy) NSString *userName;
-
对于宏定义的常量
- #define 预处理定义的常量全部大写,单词间用 _ 分隔
- 宏定义中如果包含表达式或变量,表达式或变量必须用小括号括起来。
-
对于类型常量
- 对于局限于某编译单元(实现文件)的常量,以字符k开头,例如
kAnimationDuration
- 对于定义于类头文件的常量,外部可见,则以定义该常量所在类的类名开头,例如
EOCViewClassAnimationDuration
, 仿照苹果风格,在头文件中进行extern
声明,在实现文件中定义其值
- 对于局限于某编译单元(实现文件)的常量,以字符k开头,例如
// 宏定义的常量
#define ANIMATION_DURATION 0.3
#define MY_MIN(A, B) ((A)>(B)?(B):(A))
// 局部类型常量
const NSTimeInterval kAnimationDuration = 0.3;
// 外部可见类型常量
// EOCViewClass.h
extern const NSTimeInterval EOCViewClassAnimationDuration;
extern NSString * const EOCViewClassStringConstant; // 字符串类型
// EOCViewClass.m
const NSTimeInterval EOCViewClassAnimationDuration = 0.3;
NSString * const EOCViewClassStringConstant = @"EOCStringConstant";
-
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,
};
-
用
delegate
做后缀,如<UIScrollViewDelegate>
-
用
optional
修饰可以不实现的方法,用required
修饰必须实现的方法 -
使用
did
和will
通知Delegate
已经发生的变化或将要发生的变化。 -
类的实例必须为回调方法的参数之一
- 回调方法的参数只有类自己的情况,方法名要符合实际含义
- 回调方法存在两个以上参数的情况,以类的名字开头,以表明此方法是属于哪个类的
@protocol UITableViewDataSource <NSObject>
@required
// 回调方法存在两个以上参数
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
@optional
// 回调方法的参数只有类自己
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
@protocol UITableViewDelegate <NSObject, UIScrollViewDelegate>
@optional
// 使用`did`和`will`通知`Delegate`
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
-
方法名用小驼峰式命名
-
方法名不要使用new作为前缀
-
不要使用and来连接属性参数,如果方法描述两种独立的行为,使用and来串接它们。
-
方法实现时,如果参数过长,则令每个参数占用一行,以冒号对齐。
-
一般方法不使用前缀命名,私有方法可以使用统一的前缀来分组和辨识
-
方法名要与对应的参数名保持高度一致
-
表示对象行为的方法、执行性的方法应该以动词开头
-
返回性的方法应该以返回的内容开头,但之前不要加get,除非是间接返回一个或多个值。
-
可以使用情态动词(动词前面can、should、will等)进一步说明属性意思,但不要使用do或does,因为这些助动词没什么实际意义。也不要在动词前使用副词或形容词修饰
// 不要使用 and 来连接属性参数
- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; // 推荐
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; // 反对
// 表示对象行为的方法、执行性的方法
- (void)insertModel:(id)model atIndex:(NSUInteger)atIndex;
- (void)selectTabViewItem:(NSTableViewItem *)tableViewItem
// 返回性的方法
- (instancetype)arrayWithArray:(NSArray *)array;
// 参数过长的情况
- (void)longMethodWith:(NSString *)theFoo
rect:(CGRect)theRect
interval:(CGFloat)theInterval
{
//Implementation
}
// 不要加get
- (NSSize)cellSize; // 推荐
- (NSSize)getCellSize; // 反对
// 使用情态动词,不要使用do或does
- (BOOL)canHide; //推荐
- (BOOL)shouldCloseDocument; //推荐
- (BOOL)doesAcceptGlyphInfo; //反对
优秀的代码大部分是可以自描述的,我们完全可以用代码本身来表达它到底在干什么,而不需要注释的辅助。
但并不是说一定不能写注释,有以下三种情况比较适合写注释:
-
公共接口(注释要告诉阅读代码的人,当前类能实现什么功能)。
-
涉及到比较深层专业知识的代码(注释要体现出实现原理和思想)。
-
容易产生歧义的代码(但是严格来说,容易让人产生歧义的代码是不允许存在的)。
除了上述这三种情况,如果别人只能依靠注释才能读懂你的代码的时候,就要反思代码出现了什么问题。
最后,对于注释的内容,相对于“做了什么”,更应该说明“为什么这么做”。
如果有一个以上的import语句,就对这些语句进行分组,每个分组的注释是可选的。
// Frameworks
#import <QuartzCore>;
// Models
#import "NYTUser.h"
// Views
#import "NYTButton.h"
#import "NYTUserView.h"
写在属性之后,用两个空格隔开
@property (nonatomic, readwrite, strong) UIView *headerView; // 注释
一个函数(方法)必须有一个字符串文档来解释,除非它:
-
非公开,私有函数。
-
很短。
-
显而易见。
而其余的,包括公开接口,重要的方法,分类,以及协议,都应该伴随文档(注释)
方法的注释使用Xcode
自带注释快捷键:Commond + Option + /
单行的用// + 空格
开头,多行的采用/* */
注释
使用// TODO: 说明
标记一些未完成的或完成的不尽如人意的地方
定义一个对象时,指针*
靠近变量
NSString *userName;
在- 、+
和返回值之间留一个空格
-
不要在工程里使用 Tab 键,使用空格来进行缩进。在 Xcode > Preferences > Text Editing 将 Tab 和自动缩进都设置为 4 个空格
-
Method与Method之间空一行
-
一元运算符与变量之间没有空格、二元运算符与变量之间必须有空格
!bValue
fLength = fWidth * 2;
- (void)sampleMethod1;
- (void)sampleMethod2;
使用#pragma mark -
对method
进行分组
#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
-
对于类的method:左括号另起一行写(遵循苹果官方文档)
-
对于其他使用场景(if,for,while,switch等): 左括号跟在第一行后边
- (void)sampleMethod
{
BOOL someCondition = YES;
if(someCondition) {
// do something here
}
}
@property (nonatomic, readwrite, strong) UIView *headerView; // 注释
- 须列出所有分支(穷举所有的情况),而且每个分支都须给出明确的结果。
var hintStr;
if (count < 3) {
hintStr = "Good";
} else {
hintStr = "";
}
- 不要使用过多的分支,要善于使用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;
- 条件过多,过长的时候应该换行。条件表达式如果很长,则需要将他们提取出来赋给一个BOOL值,或者抽取出一个方法
if (condition1 &&
condition2 &&
condition3 &&
condition4) {
// Do something
}
BOOL finalCondition = condition1 && condition2 && condition3 && condition4
if (finalCondition) {
// Do something
}
if ([self canDelete]){
// Do something
}
- (BOOL)canDelete
{
BOOL finalCondition1 = condition1 && condition2
BOOL finalCondition2 = condition3 && condition4
return condition1 && condition2;
}
- 每个分支的实现代码都须被大括号包围
if (!error) {
return success;
}
- 不可在for循环内修改循环变量,防止for循环失去控制。
for (int index = 0; index < 10; index++){
...
logicToChange(index)
}
- 在有返回值的方法里消除break:将break转换为return立即返回
func hasBadProductIn(products: Array<String>) -> Bool {
var result = false
for level in products {
if level.hasPrefix("bad") {
result = true
break
}
}
return result
}
- 每个分支都必须用大括号括起来
switch (integer) {
case 1: {
// ...
}
break;
case 2: {
// ...
}
break;
default: {
// ...
break;
}
}
- 一个函数只做一件事(单一原则)
每个函数的职责都应该划分的很明确(就像类一样)。
dataConfiguration()
viewConfiguration()
// 不推荐
void dataConfiguration()
{
...
viewConfiguration()
}
- 对于有返回值的函数(方法),每一个分支都必须有返回值
int function()
{
if (condition1) {
return count1
} else if(condition2) {
return count2
} else {
return defaultCount
}
}
// 不推荐
int function()
{
if (condition1) {
return count1
} else if(condition2) {
return count2
}
}
- 对输入参数的正确性和有效性进行检查,参数错误立即返回
void function(param1,param2)
{
if(param1 is unavailable){
return;
}
if(param2 is unavailable){
return;
}
//Do some right thing
}
-
如果在不同的函数内部有相同的功能,应该把相同的功能抽取出来单独作为另一个函数
-
将函数内部比较复杂的逻辑提取出来作为单独的函数