【iOS】iOS客户端代码规范建议
1.1 命名
-
属性、变量、方法均使用小写字母开头的驼峰命名,推荐使用长的、描述性的方法和变量名。
-
私有方法不建议以下划线开头,因为这是C++标准,且下划线前缀是Apple保留的,不要冒重载苹果的私有方法的险。
-
执行性的方法以动词开头,返回性的方法以返回内容开头,但之前不要加get
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;
+ (id)arrayWithArray:(NSArray *)array;
- 类名、协议名、枚举类型、宏统一以项目前缀开头,项目前缀为2-3个大写字母,例如FJK。这样做的目的是因为objc是没有命名空间。
@interface FJKBaseViewController : UIViewController
@end
-
常量命名,常量应该使用驼峰命名法,并且为了清楚,应该用相关的类名作为前缀。常量应该尽量使用一致的字符串字面值或者数字,这样便于经常用到的时候复用,并且可以快速修改而避免查找和替换。 常量应该用
static声明,不要使用#define,除非它就是明确作为一个宏来用的。// 推荐 static const NSTimeInterval FJKSignInViewControllerFadeOutAnimationDuration = 0.4; // 不推荐 static const NSTimeInterval fadeOutTime = 0.4; -
宏的命名,主要是两种,语义较复杂时,推荐第二种,可读性更好。
// C语言风格 大写字母拼下划线 #define FG_SCREEN_SIZE [[UIScreen mainScreen] bounds].size // objc风格,驼峰式 #define FGUserDefaults [NSUserDefaults standardUserDefaults]
1.2 属性
- 指针"*"号的位置在变量名前,而非变量类型之后,与变量名无空格相连,与类型间有个空格:
- @property括号内的描述性修饰符,严格按以下顺序书写:原子性,读写,内存管理, 因为三种描述符经常需要修改,属性与属性间差异比较大的是内存管理,其次才是读写和原子性,方便从右往左修改,也能让代码前部分较美观地对齐
- 不可变类型,但可被可变类型(Mutable)对象赋值的属性,例如:NSString、NSArray、NSDictionary、NSURLRequest,其内存管理属性类型必须为copy。 以防止声明的不可变的属性,实际赋值的是一个可变对象,对象内容还在不知情的情况下被外部修改。
@property (nonatomic, readwrite, copy) NSString *password;
1.3 运算符
除了++和--外的运算符前后均需要一个空格。
1.4 流程控制
- 花括号统一采用不换行风格。 涉及位置:@interface、方法实现、if-else、switch-case等。 优点: 减少代码冗余行数,代码更加紧凑,结构清晰。
if ( i > 10 ) {
i++;
} else {
i += 2;
}
for (int i = 0; i < 10; i++) {
NSLog(@"%d",i);
}
- if、else后面必须紧跟"{ }",即便只有一行代码。 防止之后增添逻辑时忽略增加{},逻辑代码跑到if、else之外,同时更便于阅读。
if (!error) {
return success;
}
// 不推荐:
if (!error)
return success;
// 不推荐
if (!error) return success;
- 不要使用尤达表达式。尤达表达式是指,拿一个常量去和变量比较而不是拿变量去和常量比较。它就像是在表达 “蓝色是不是天空的颜色” 或者 “高个是不是这个男人的属性” 而不是 “天空是不是蓝的” 或者 “这个男人是不是高个子的”
// 推荐
if ([myValue isEqual:@42]) { ...
// 不推荐
if ([@42 isEqual:myValue]) { ...
- if语句在比较时,括号内不使用nil、NO或YES
// 不推荐
if (obj == nil && finish == YES && result == NO) {
}
// 推荐
if (!obj && finish && !result) { // good
}
-
当编写条件语句的时候,左边的代码间距应该是一个“黄金”或者“快乐”的大道。 这是说,不要嵌套
if语句。多个 return 语句是 OK 的。这样可以避免 Cyclomatic 复杂性 (译者注: https://en.wikipedia.org/wiki/Cyclomatic_complexity),并且让代码更加容易阅读。因为你的方法的重要部分没有嵌套在分支上,你可以很清楚地找到相关的代码。// 推荐 - (void)someMethod { if (![someOther boolValue]) { return; } //Do something important } // 不推荐 - (void)someMethod { if ([someOther boolValue]) { //Do something important } }-
Case语句,当一个 case 包含了多行语句的时候,需要加上括号。
switch (condition) { case 1: // ... break; case 2: { // ... // Multi-line example using braces break; } case 3: // ... break; default: // ... break; }
-
1.5 注释
- 单行注释,“//”之后空一格,再写具体注释内容,如果“//”注释与代码在同一行,则代码最后一个字符空一格,再写注释
- 对方法签名使用多行注释,按照Xocde风格“Editor-Structure-Add Document”添加。Description部分,第一行用最简单语句描述方法作用,如需详细说明,则空一行后,再进行详细描述
// 单行注释
// 不推荐
- (void)test {
//Just For Debug
BOOL isTest = YES;//Main Logic
//...
}
// 推荐
- (void)test {
// Just For Debug
BOOL isTest = YES; // Main Logic
// ...
}
// 多行注释
/**
执行xxx操作,可能失败。
xxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxx(具体使用事项)
@param error NSError,-1:xxx;-2:xxxxx
*/
-
对于需要提醒后来这的地方,应注释:
// FIXME: 该处需要注意!对于某处代码有未完成或有待优化的地方,应注释:
// TODO: 该处尚未完成 -
测试或者DEBUG代码应加入注释
// DEBUG: 你写这段debug代码目的-
方法注释推荐使用Xcode的快捷方法:option+command+/
1.6 Dealloc
viewController类建议重写dealloc方法
dealloc方法的实现,需要放在文件最前面,一般在@implementation之后,在init或viewDidLoad之前,以便于检查。
1.7 接口
-
保持公有API简明:对于不想公开的方法和属性,只在.m文件中声明实现,.h文件中仅声明必须公开的方法/属性。
-
委托模式中声明的@protocol名称,需要以委托类名(比如UITableView)开头,之后加上“Delegate”或“Protocol”。
@protocol内声明的方法,需要以委托类名去除项目前缀后的单词开头,并且第一个参数需要为委托对象,否则被委托类代理了多个委托时,无法区分该委托方法是由哪个委托对象发起的。@class CSShareViewController; @protocol CSShareDelegate <NSObject> // 不推荐 - (void)shareFinished:(BOOL)isSuccess; // 不推荐 @end @class CSShareViewController; @protocol CSShareViewControllerDelegate <NSObject> // 推荐 - (void)shareViewController:(CSShareViewController *)shareViewController shareFinished:(BOOL)isSuccess; // 推荐 @end
1.8 代码组织
对一个文件中的代码使用“#pragma mark - CodeBlockName ”进行分段,易于代码维护和阅读。
建议代码组织顺序为LifeCycle、Public Methods、Private Methods、Delegates
#pragma mark - CodeBlockName
或者
// MARK: - codeBlockName
1.9 方法
- 对于方法签名,在方法类型 (
-/+符号)后应该要有一个空格。方法段之间也应该有一个空格(来符合 Apple 的规范)。在参数名称之前总是应该有一个描述性的关键词。
// 推荐
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
- (void)sendAction:(SEL)aSelector to:(id)anObject forAllCells:(BOOL)flag;
- (id)viewWithTag:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
// 不推荐
- (void)setT:(NSString *)text i:(UIImage *)image;
- (void)sendAction:(SEL)aSelector :(id)anObject :(BOOL)flag;
- (id)taggedView:(NSInteger)tag;
- (instancetype)initWithWidth:(CGFloat)width andHeight:(CGFloat)height;
- (instancetype)initWith:(int)width and:(int)height; // Never do this.
- 下面的例子应该被避免
NSMutableArray *aMutableArray = [@[] mutableCopy];
上面的书写方式存在效率以及可读性的问题。效率方面,一个不必要的不可变变量被创建,并且马上被废弃了;这并不会让你的 App 变得更慢(除非这个方法会被很频繁地调用),但是确实没必要为了少打几个字而这样做。
- 采用字面值更加简洁
// 推荐
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal", @"Mobile Web" : @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingZIPCode = @10018;
// 不推荐
NSArray *names = [NSArray arrayWithObjects:@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul", nil];
NSDictionary *productManagers = [NSDictionary dictionaryWithObjectsAndKeys: @"Kate", @"iPhone", @"Kamal", @"iPad", @"Bill", @"Mobile Web", nil];
NSNumber *shouldUseLiterals = [NSNumber numberWithBool:YES];
NSNumber *buildingZIPCode = [NSNumber numberWithInteger:10018];