参考
The official raywenderlich.com Objective-C style guide.
Objective-C编码规范:26个方面解决iOS开发问题
自动格式化工具:XcodeClangFormat
Coding Guidelines for Cocoa
规范
-
统一使用US英语。
符合规则的:
UIColor *myColor = [UIColor whiteColor];
不符合规则的:
UIColor *myColour = [UIColor whiteColor]; UIColor *yanse = [UIColor whiteColor];
#import "xxx.h"
的排序:以对应的头文件开始,之后是项目内创建的其他文件,下一部分为所有的 Category 文件,最后是第三方库文件。本项目所创建文件部分的排序为:Controller 层文件,View 层文件,Model 层文件,数据层文件,工具类文件。Category 文件部分的排序为:项目内文件的 Category,Foundation 类的 Category,UIKit 类的 Category,第三方库类的 Category。-
使用
#pragma mark -
对方法进行分类。符合规则的:
#pragma mark - Override //重写方法 - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {} #pragma mark - Public //公有方法 - (void)publicMethod {} #pragma mark - Responder //响应事件的方法,包括处理通知的方法 - (IBAction)submitData:(id)sender {} - (void)handleNotification:(NSNotification *)notification {} #pragma mark - Delegate //代理实现方法(以相应协议名命名) #pragma mark - Private //私有方法 - (void)privateMethod {} #pragma mark - Setter //Setter方法 - (void)setCustomProperty:(id)value {} #pragma mark - Getter //Getter方法,通常实现懒加载 - (id)customProperty {}
-
在.h文件中方法都为 Public,若较多则以功能划分,与
#pragma mark -
间不空行,@property
间不空行,但两部分之间空一行。符合规则的:
@property NSString (nonatomic) *name; @property NSInteger length; #pragma mark - Category - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
不符合规则的:
@property NSString (nonatomic) *name; @property NSInteger length; #pragma mark - Category - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
-
在.m文件中方法以及
#pragma mark -
间空一行。符合规则的:
#pragma mark - Override - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
不符合规则的:
#pragma mark - Override - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
-
@property的默认属性值不写,只注明非默认值。
符合规则的:
@property (weak, nonatomic) NSString *property1; @property (nonatomic) NSString *property2; @property NSString *property3;
不符合规则的:
@property (strong, atomic) NSString *property1;
-
选择,循环结构语句块与上一条语句之间空一行,若在方法的第一行则不需要空行。
符合规则的:
NSInteger a; NSInteger b; if (a > b) { NSLog(@"a > b"); }
不符合规则的:
NSInteger a; NSInteger b; if (a > b) { NSLog(@"a > b"); }
-
block 代码块与上一条语句之间空一行,若在方法的第一行则不需要空行。
符合规则的:
NSInteger a; NSInteger b; [UIView animateWithDuration:1.0 animations:^{ // something }];
不符合规则的:
NSInteger a; NSInteger b; [UIView animateWithDuration:1.0 animations:^{ // something }];
缩进使用4个空格,可在 Xcode 中的
Preferences->Text Editing->Indentation
中设置。空行中不要留有空字符,可在 Xcode 中的
Preferences->Text Editing->Editing
中设置。-
方法及其他语句块的大括号总是在同一行语句打开但在新行中关闭。
符合规则的:
if (success) { //Do something } else { //Do something else }
不符合规则的:
if (success) { //Do something } else { //Do something else }
-
选择,循环结构语句块中关键字,条件,大括号之间都有一个空格。
符合规则的:
if (success) { //Do something } else { //Do something else }
不符合规则的:
if(success){ //Do something }else{ //Do something else }
-
return语句前空一行。
符合规则的:
- (NSString *)method { NSString *string = @""; return string; }
不符合规则的:
- (NSString *)method { NSString *string = @""; return string; }
-
避免以冒号对齐的方式来声明,定义和调用方法。
符合规则的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { } // blocks are easily readable [UIView animateWithDuration:1.0 animations:^{ // something } completion:^(BOOL finished) { // something }];
不符合规则的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { } // colon-aligning makes the block indentation hard to read [UIView animateWithDuration:1.0 animations:^{ // something } completion:^(BOOL finished) { // something }];
-
方法定义时,修饰符与返回类型之间有一个空格,方法名与其后的开始大括号之间有一个空格。在方法各个段之间应该也有一个空格,在参数之前应该包含一个具有描述性的关键字来描述参数。不要使用and作为多个参数的说明。各参数段的说明,首字母也要小写。
符合规则的:
- (void)method { } - (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)method{ } -(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.
-
统一使用语法糖。特别注意 nil 值不能传入 NSArray 和
NSDictionary 字面值,因为这样会导致 crash。符合规则的:
@property (nonatomic) NSString *string; - (void)text { NSString *localString = self.string; self.string = @""; NSNumber *number = @(1); NSDictionary *dictionary = @{@"value1":@"key1"}; NSString *value1 = dictionary[@"key1"]; NSArray *array = @[@"value"]; NSString *value = array[1]; }
不符合规则的:
@property (nonatomic) NSString *string; - (void)text { NSString *localString = [self string]; [self setString:@""]; NSNumber *number = [NSNumber numberWithInt:1]; NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"value1",@"key1", nil]; NSString *value1 = [dictionary objectForKey:@"key1"]; NSArray *array = [NSArray arrayWithObjects:@"value", nil]; NSArray *array = @[@"value", nil]; NSString *value = [array objectAtIndex:1]; }
注释应该用来解释这段特殊代码为什么要这样做,而不是翻译代码做了什么。任何被使用的注释都必须保持最新或被删除。
-
由于Objective-C中没有命名空间的概念,所以一般会在所有类前加一个项目自定义的大写缩写前缀,一般为两到三个字符。尽量使用描述完整的变量名(包括property)和方法名,因为有编辑器的自动提示功能,可以允许命名较长,使用驼峰式命名规则,但首字母要小写,变量命名的一个原则是在变量名的最后指明变量的类型。
符合规则的:
UIButton *settingsButton; NSArray *viewArray = [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
不符合规则的:
UIButton *setBut; NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
-
常量应该使用驼峰式命名规则。全局常量以小写的字母
k
加项目前缀开头,一个类内部使用的静态常量则不需要加项目前缀,之后所有的单词首字母大写。符合规则的:
NSString const* kSRMConstString = @""; static NSString const* kSRMStaticConstString = @"";
不符合规则的:
static NSString const* constString = @"";
局部变量的命名不要以下划线开始,以便和 property 默认生成的成员变量区别。除了 property 的 setter 和 getter 方法外,如果没有特殊情况,不要使用默认生成的变量,应直接操作属性。
-
统一使用属性,避免使用成员变量。属性完全可以替代成员变量,当不对 setter 和 getter 方法进行自定义时,属性与成员变量效果一致,使用属性可以灵活的随时加以额外的处理。
符合规则的:
@interface SRClass: NSObject @property (nonatomic) NSString *variable; @end
不符合规则的:
@interface SRClass: NSObject { NSString *variable; }
-
星号表示声明的变量是指针类型
符合规则的:
NSString *string = @"";
不符合规则的:
NSString * string = @""; NSString* string = @"";
特殊情况:
NSString *const string= @"";
-
NSString、NSDictionary、NSArray 应该使用 copy 属性特性。即使你声明的是以上类型的属性,有人可能传入一个对应的可变版本的实例,然后在你没有注意的情况下修改它
符合规则的:
@property (copy, nonatomic) NSString *variable;
不符合规则的:
@property (nonatomic) NSString *variable;
关于代码段中其他情况的空行,super 的方法可以在方法中的任意位置调用,所以 super 方法调用的代码前后不需要空行,原则上除相应语法代码段的空行外在方法中不出现其他空行,如果两个代码段之间需要空行区分,考虑是否应分成两个新方法。如果真的有需要空行区分,可以加一行简单注释分隔,不空行。
-
不使用宏来定义常量,常量是容易重复被使用且无需通过查找和代替就能快速修改值。常量应该使用 const 来声明而不是使用#define。
符合规则的:
NSString *const kSRString = @"string"; static CGFloat const kStaticFloat = 50.0;
不符合规则的:
#define kSRString @"string" #define kSRStaticFloat 50.0
-
文件内部使用的字符串常量
static NSString *const kFoo = @"Foo";
供多个模块使用的公共字符串常量,名称要加项目缩写前缀
// .m 文件中 NSString *const Foo = @"Foo"; // .h 文件中 extern NSString *const Foo;
常量名以小写字母 k 开头是参照匈牙利命名法(Hungarian Notation),表示常量 constant 的意思。内部常量使用这一规则,以便和变量名进行区分。UIKit 不使用这一规范,直接以缩写前缀开始。所以公共常量直接以缩写前缀开始。
-
当定义枚举类型时,使用新的固定基本类型规格,因为它有更强的类型检查和代码补全。现在SDK有一个宏 NS_ENUM() 来帮助和鼓励你使用固定的基本类型。
符合规则的:
typedef NS_ENUM(NSInteger, SREnumType) { SREnumTypeA, SREnumTypeB, SREnumTypeC };
不符合规则的:
enum SREnumType { SREnumTypeA, SREnumTypeB, SREnumTypeC };
-
当在 case 语句块中声明变量,则该 case 语句块必须被大括号包围,若一个
case 语句块中没有声明变量,则不加大括号。当switch枚举所有类型时,'default'是不需要的符合规则的:
switch(SREnumType) { case SREnumTypeA: case SREnumTypeB: { NSString *emptyString = @""; // code } break; case SREnumTypeC: // code break; }
-
私有属性应该在类的实现文件中的类扩展(匿名分类)中声明。
符合规则的:
@interface SRViewController () @property (nonatomic) GADBannerView *googleAdView; @property (nonatomic) ADBannerView *iAdView; @property (nonatomic) UIWebView *adXWebView; @end
-
Objective-C 使用 YES 和 NO。因为 true 和 false 应该只在
CoreFoundation,C 或 C++ 代码使用。既然 nil 解析成 NO,所以没有必要在条件语句比较。不要拿某样东西直接与 YES 比较,因为YES被定义为1而一个 BOOL
能被设置为8位。符合规则的:
if (someObject) { } if (![anotherObject boolValue]) { }
不符合规则的:
if (someObject == nil) {} if ([anotherObject boolValue] == NO) {} if (isAwesome == YES) {} // Never do this. if (isAwesome == true) {} // Never do this.
-
条件语句主体必须使用大括号包围,即使只有一行代码
符合规则的:
if (!error) { return success; }
不符合规则的:
if (!error) return success; //or if (!error) return success;
-
当需要提高代码的清晰性和简洁性时,三元操作符
?:
才会使用。单个条件求值常常需要它。多个条件求值时,如果使用if语句或重构成实例变量时,代码会更加易读。一般来说,最好使用三元操作符是在根据条件来赋值的情况下。Non-boolean的变量与某东西比较,加上括号()会提高可读性。如果被比较的变量是boolean类型,那么就不需要括号。符合规则的:
NSInteger value = 5; result = (value != 0) ? x : y; BOOL isHorizontal = YES; result = isHorizontal ? x : y;
不符合规则的:
result = a > b ? x = c > d ? c : d : y;
-
Init 方法应该遵循 Apple 生成代码模板的命名规则,返回类型应该使用 instancetype 而不是 id。
符合规则的:
- (instancetype)init { self = [super init]; if (self) { // ... } return self; }
-
当类构造方法被使用时,它应该返回类型是 instancetype 而不是
id。这样确保编译器正确地推断结果类型。符合规则的:
@interface Airplane + (instancetype)airplaneWithType:(RWTAirplaneType)type; @end
-
当访问 CGRect 里的 x、y、width 或 height 时,应该使用CGGeometry 函数而不是直接通过结构体来访问。引用Apple的CGGeometry:
在这个参考文档中所有的函数,接受CGRect结构体作为输入,在计算它们结果时隐式地标准化这些rectangles。因此,你的应用程序应该避免直接访问和修改保存在CGRect数据结构中的数据。相反,使用这些函数来操纵rectangles和获取它们的特性。
符合规则的:
CGRect frame = self.view.frame; CGFloat x = CGRectGetMinX(frame); CGFloat y = CGRectGetMinY(frame); CGFloat width = CGRectGetWidth(frame); CGFloat height = CGRectGetHeight(frame); CGRect frame = CGRectMake(0.0, 0.0, width, height);
不符合规则的:
CGRect frame = self.view.frame; CGFloat x = frame.origin.x; CGFloat y = frame.origin.y; CGFloat width = frame.size.width; CGFloat height = frame.size.height; CGRect frame = (CGRect){ .origin = CGPointZero, .size = frame.size };
-
当使用条件语句编码时,左手边的代码应该是"golden" 或 "happy"路径。也就是不要嵌套if语句,多个返回语句也是OK。
符合规则的:
- (void)someMethod { if (![someOther boolValue]) { return; } //Do something important }
不符合规则的:
- (void)someMethod { if ([someOther boolValue]) { //Do something important } }
-
当方法通过引用来返回一个错误参数,判断返回值而不是错误变量。在成功的情况下,有些Apple的APIs记录垃圾值(garbage values)到错误参数(如果non-NULL),那么判断错误值会导致false负值和crash。
符合规则的:
NSError *error; if (![self trySomethingWithError:&error]) { // Handle Error }
不符合规则的:
NSError *error; [self trySomethingWithError:&error]; if (error) { // Handle Error }
-
单例对象应该使用线程安全模式来创建共享实例。
符合规则的:
+ (instancetype)sharedInstance { static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }
不符合规则的:
+ (instancetype)sharedInstance { static MyClass *shared = nil; if(shared == nil) { shared = [[MyClass alloc] init]; } return shared; }
物理文件目录应该与 Xcode 工程文件目录保持同步来避免文件扩张。任何Xcode分组的创建应该在文件系统的文件体现。代码不仅是根据类型来分组,而且还可以根据功能来分组,这样代码更加清晰。
-
除了使用带参数的init方法初始化实例时与alloc方法链式调用,其他情况一般不链式调用。
符合规则的:
MyClass *myClass = [[MyClass alloc] initWithName:@""]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self selector:@selector(disposeNotification:) name:SRNotification object:nil];
不符合规则的:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disposeNotification:) name:SRNotification object:nil];
-
当变量不使用默认的 strong 进行修饰时,需明确写出指定的修饰符
符合规则的:
MyClass * __weak myWeakReference; MyClass * __unsafe_unretained myUnsafeReference;
不符合规则的:
__weak MyClass *myWeakReference; __unsafe_unretained MyClass *myUnsafeReference;