Objective-C编码规范
前段时间接手了一个开发三年之久的老项目,三年期间经历了5-6个程序员的开发,整个项目架构比较混乱,代码风格差距很大,也有许多不规范代码,难以Code Review。对于承担这个项目的维护、部分代码重构以及后续产品开发的责任,我拟定了这个迟到了三年之久的项目代码规范,要求所有的iOS同事在之后的开发以及代码的重构过程中以此作为编码规范。此代码规范一直是我自身的代码规范,来源于几年前借鉴 raywenderlich的Objective-C style ,感兴趣的朋友可以去GitHub上自行搜索。
命名###
- 变量的命名使用US英语,不可使用拼音代替;
- 尽可能坚持Apple的命名规则,长的,描述性的方法和变量命名是最好的
应该:
UIButton *nextButton;
UIlabel *nameLabel;
或者可以使用简介的命名方式:
UIButton *btn_next;
UILabel *lb_name;
切不可随意命名控件:
UIButton *next;
UILabel *name; - 变量的命名采用驼峰命名法则,并且单词的首字母需要小写
- 星号表示变量是指针。例如,
NSString *text
既不是NSString* text
也不是NSString * text
,除了一些特殊情况下常量。
下划线###
- 当使用属性时,实例变量应该使用self.来访问和改变。这就意味着所有属性将会视觉效果不同,因为它们前面都有self.当然,重写属性的set,get方法中等必须使用下划线的情况下使用下划线.
代码组织###
-
使用#pragma mark -来分类方法,使得文件结构清晰,遵循以下一般结构:
#pragma mark - Lifecycle- (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {} #pragma mark - Custom Accessors - (void)setCustomProperty:(id)value {} - (id)customProperty {} #pragma mark - IBActions - (IBAction)submitData:(id)sender {} #pragma mark - Public - (void)publicMethod {} #pragma mark - Private - (void)privateMethod {} #pragma mark - Protocol conformance #pragma mark - UITextFieldDelegate #pragma mark - UITableViewDataSource #pragma mark - UITableViewDelegate #pragma mark - NSCopying - (id)copyWithZone:(NSZone *)zone {} #pragma mark - NSObject - (NSString *)description {}
-
注释: .h文件中必须要有注释,标明属性方法等的含义,.m文件中选择性适当注释业务逻辑。当需要注释时,注释应该用来解释这段特殊代码为什么要这样做。任何被使用的注释都必须保持最新或被删除。
-
方法之间的间隔
在方法签名中,应该在方法类型(-/+ 符号)之后有一个空格。在方法各个段之间应该也有一个空格(符合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. -
在方法之间应该有且只有一行,这样有利于在视觉上更清晰和更易于组织。在方法内的空白应该分离功能,但通常都抽离出来成为一个新方法。
-
缩进使用4个空格,确保在Xcode偏好设置来设置,确保代码对齐。
-
方法大括号和其他大括号(if/else/switch/while 等.)总是在同一行语句打开但在新行中关闭
应该:
if (user.isHappy) {
//Do something
} else {
//Do something else
}
不应该:
if (user.isHappy)
{
//Do something
}
else {
//Do something else
} -
应该避免以冒号对齐的方式来调用方法。因为有时方法签名可能有3个以上的冒号和冒号对齐会使代码更加易读。请不要这样做,尽管冒号对齐的方法包含代码块,因为Xcode的对齐方式令它难以辨认。
应该:
// blocks are easily readable
[UIView animateWithDuration:1.0 animations:^{
// something
} completion:^(BOOL finished) {
// something
}];
不应该:
// colon-aligning makes the block indentation hard to read
[UIView animateWithDuration:1.0
animations:^{
// something
}
completion:^(BOOL finished) {
// something
}];
变量###
- 私有变量应该尽可能代替实例变量的使用。尽管使用实例变量是一种有效的方式,但更偏向于使用属性来保持代码一致性。
应该:
@interface RWTTutorial : NSObject
@property (strong, nonatomic) NSString *tutorialName;
@end
不应该:
@interface RWTTutorial : NSObject {
NSString *tutorialName;
}
字面值###
- NSString, NSDictionary, NSArray, 和 NSNumber的字面值应该在创建这些类的不可变实例时被使用。请特别注意nil值不能传入NSArray和NSDictionary字面值,因为这样会导致crash。
应该:
NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve", @"Paul"];
NSDictionary *productManagers = @{@"iPhone": @"Kate", @"iPad": @"Kamal", @"Mobile Web": @"Bill"};
NSNumber *shouldUseLiterals = @YES;
NSNumber *buildingStreetNumber = @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 *buildingStreetNumber = [NSNumber numberWithInteger:10018];
枚举类型###
- 当使用enum时,推荐使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏NS_ENUM()来帮助和鼓励你使用固定的基本类型。
例如:
typedef NS_ENUM(NSInteger, RWTLeftMenuTopItemType) {
RWTLeftMenuTopItemMain,
RWTLeftMenuTopItemShows,
RWTLeftMenuTopItemSchedule
};
或者显式地赋值:
typedef NS_ENUM(NSInteger, RWTGlobalConstants) {
RWTPinSizeMin = 1,
RWTPinSizeMax = 5,
RWTPinCountMin = 100,
RWTPinCountMax = 500,
};
不应该:
enum GlobalConstants {
kMaxPinSize = 5,
kMaxPinCount = 500,
};
条件语句###
- 只用一行代码,也应该使用大括号包围。一方面,可能发生在if语句里面一行代码被注释了,然后下一行代码不知不觉地成为if语句的一部分。另一方面,这种风格与其他条件语句的风格保持一致,所以更加容易阅读。
应该:
if (!error) {
return success;
}
不应该:
if (!error)
return success;
也不应该:
if (!error) return success;
黄金路径###
- 当使用条件语句编码时,不要嵌套if语句,多个返回语句也是OK。
应该:
- (void)someMethod {
if (![someOther boolValue]) {
return;
}
//Do something important
}
不应该:
- (void)someMethod {
if ([someOther boolValue]) {
//Do something important
}
}
Xcode工程###
- 物理文件应该与Xcode工程文件保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。