參考
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;