參考
The official raywenderlich.com Objective-C style guide.
Objective-C編碼規(guī)范:26個(gè)方面解決iOS開發(fā)問題
自動(dòng)格式化工具:XcodeClangFormat
Coding Guidelines for Cocoa
規(guī)范
-
統(tǒng)一使用US英語徐伐。
符合規(guī)則的:
UIColor *myColor = [UIColor whiteColor];
不符合規(guī)則的:
UIColor *myColour = [UIColor whiteColor]; UIColor *yanse = [UIColor whiteColor];
#import "xxx.h"
的排序:以對(duì)應(yīng)的頭文件開始潘拨,之后是項(xiàng)目?jī)?nèi)創(chuàng)建的其他文件谐檀,下一部分為所有的 Category 文件,最后是第三方庫(kù)文件纯趋。本項(xiàng)目所創(chuàng)建文件部分的排序?yàn)椋篊ontroller 層文件,View 層文件冷离,Model 層文件吵冒,數(shù)據(jù)層文件,工具類文件酒朵。Category 文件部分的排序?yàn)椋喉?xiàng)目?jī)?nèi)文件的 Category桦锄,F(xiàn)oundation 類的 Category,UIKit 類的 Category蔫耽,第三方庫(kù)類的 Category结耀。-
使用
#pragma mark -
對(duì)方法進(jìn)行分類留夜。符合規(guī)則的:
#pragma mark - Override //重寫方法 - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {} #pragma mark - Public //公有方法 - (void)publicMethod {} #pragma mark - Responder //響應(yīng)事件的方法,包括處理通知的方法 - (IBAction)submitData:(id)sender {} - (void)handleNotification:(NSNotification *)notification {} #pragma mark - Delegate //代理實(shí)現(xiàn)方法(以相應(yīng)協(xié)議名命名) #pragma mark - Private //私有方法 - (void)privateMethod {} #pragma mark - Setter //Setter方法 - (void)setCustomProperty:(id)value {} #pragma mark - Getter //Getter方法图甜,通常實(shí)現(xiàn)懶加載 - (id)customProperty {}
-
在.h文件中方法都為 Public碍粥,若較多則以功能劃分,與
#pragma mark -
間不空行黑毅,@property
間不空行嚼摩,但兩部分之間空一行。符合規(guī)則的:
@property NSString (nonatomic) *name; @property NSInteger length; #pragma mark - Category - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
不符合規(guī)則的:
@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 -
間空一行矿瘦。符合規(guī)則的:
#pragma mark - Override - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
不符合規(guī)則的:
#pragma mark - Override - (instancetype)init {} - (void)dealloc {} - (void)viewDidLoad {} - (void)viewWillAppear:(BOOL)animated {} - (void)didReceiveMemoryWarning {}
-
@property的默認(rèn)屬性值不寫枕面,只注明非默認(rèn)值。
符合規(guī)則的:
@property (weak, nonatomic) NSString *property1; @property (nonatomic) NSString *property2; @property NSString *property3;
不符合規(guī)則的:
@property (strong, atomic) NSString *property1;
-
選擇缚去,循環(huán)結(jié)構(gòu)語句塊與上一條語句之間空一行潮秘,若在方法的第一行則不需要空行。
符合規(guī)則的:
NSInteger a; NSInteger b; if (a > b) { NSLog(@"a > b"); }
不符合規(guī)則的:
NSInteger a; NSInteger b; if (a > b) { NSLog(@"a > b"); }
-
block 代碼塊與上一條語句之間空一行易结,若在方法的第一行則不需要空行枕荞。
符合規(guī)則的:
NSInteger a; NSInteger b; [UIView animateWithDuration:1.0 animations:^{ // something }];
不符合規(guī)則的:
NSInteger a; NSInteger b; [UIView animateWithDuration:1.0 animations:^{ // something }];
縮進(jìn)使用4個(gè)空格,可在 Xcode 中的
Preferences->Text Editing->Indentation
中設(shè)置搞动。空行中不要留有空字符躏精,可在 Xcode 中的
Preferences->Text Editing->Editing
中設(shè)置。-
方法及其他語句塊的大括號(hào)總是在同一行語句打開但在新行中關(guān)閉鹦肿。
符合規(guī)則的:
if (success) { //Do something } else { //Do something else }
不符合規(guī)則的:
if (success) { //Do something } else { //Do something else }
-
選擇矗烛,循環(huán)結(jié)構(gòu)語句塊中關(guān)鍵字,條件狮惜,大括號(hào)之間都有一個(gè)空格高诺。
符合規(guī)則的:
if (success) { //Do something } else { //Do something else }
不符合規(guī)則的:
if(success){ //Do something }else{ //Do something else }
-
return語句前空一行。
符合規(guī)則的:
- (NSString *)method { NSString *string = @""; return string; }
不符合規(guī)則的:
- (NSString *)method { NSString *string = @""; return string; }
-
避免以冒號(hào)對(duì)齊的方式來聲明碾篡,定義和調(diào)用方法虱而。
符合規(guī)則的:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { } // blocks are easily readable [UIView animateWithDuration:1.0 animations:^{ // something } completion:^(BOOL finished) { // something }];
不符合規(guī)則的:
- (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 }];
-
方法定義時(shí),修飾符與返回類型之間有一個(gè)空格开泽,方法名與其后的開始大括號(hào)之間有一個(gè)空格牡拇。在方法各個(gè)段之間應(yīng)該也有一個(gè)空格,在參數(shù)之前應(yīng)該包含一個(gè)具有描述性的關(guān)鍵字來描述參數(shù)穆律。不要使用and作為多個(gè)參數(shù)的說明惠呼。各參數(shù)段的說明,首字母也要小寫峦耘。
符合規(guī)則的:
- (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;
不符合規(guī)則的:
-(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.
-
統(tǒng)一使用語法糖剔蹋。特別注意 nil 值不能傳入 NSArray 和
NSDictionary 字面值,因?yàn)檫@樣會(huì)導(dǎo)致 crash辅髓。符合規(guī)則的:
@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]; }
不符合規(guī)則的:
@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]; }
注釋應(yīng)該用來解釋這段特殊代碼為什么要這樣做泣崩,而不是翻譯代碼做了什么篙螟。任何被使用的注釋都必須保持最新或被刪除窘疮。
-
由于Objective-C中沒有命名空間的概念策精,所以一般會(huì)在所有類前加一個(gè)項(xiàng)目自定義的大寫縮寫前綴俯萌,一般為兩到三個(gè)字符。盡量使用描述完整的變量名(包括property)和方法名买优,因?yàn)橛芯庉嬈鞯淖詣?dòng)提示功能妨马,可以允許命名較長(zhǎng),使用駝峰式命名規(guī)則杀赢,但首字母要小寫烘跺,變量命名的一個(gè)原則是在變量名的最后指明變量的類型。
符合規(guī)則的:
UIButton *settingsButton; NSArray *viewArray = [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
不符合規(guī)則的:
UIButton *setBut; NSArray *views = [[NSBundle mainBundle] loadNibNamed:@"" owner:nil options:nil];
-
常量應(yīng)該使用駝峰式命名規(guī)則葵陵。全局常量以小寫的字母
k
加項(xiàng)目前綴開頭液荸,一個(gè)類內(nèi)部使用的靜態(tài)常量則不需要加項(xiàng)目前綴,之后所有的單詞首字母大寫脱篙。符合規(guī)則的:
NSString const* kSRMConstString = @""; static NSString const* kSRMStaticConstString = @"";
不符合規(guī)則的:
static NSString const* constString = @"";
局部變量的命名不要以下劃線開始,以便和 property 默認(rèn)生成的成員變量區(qū)別伤柄。除了 property 的 setter 和 getter 方法外绊困,如果沒有特殊情況,不要使用默認(rèn)生成的變量适刀,應(yīng)直接操作屬性秤朗。
-
統(tǒng)一使用屬性,避免使用成員變量笔喉。屬性完全可以替代成員變量取视,當(dāng)不對(duì) setter 和 getter 方法進(jìn)行自定義時(shí),屬性與成員變量效果一致常挚,使用屬性可以靈活的隨時(shí)加以額外的處理作谭。
符合規(guī)則的:
@interface SRClass: NSObject @property (nonatomic) NSString *variable; @end
不符合規(guī)則的:
@interface SRClass: NSObject { NSString *variable; }
-
星號(hào)表示聲明的變量是指針類型
符合規(guī)則的:
NSString *string = @"";
不符合規(guī)則的:
NSString * string = @""; NSString* string = @"";
特殊情況:
NSString *const string= @"";
-
NSString、NSDictionary奄毡、NSArray 應(yīng)該使用 copy 屬性特性折欠。即使你聲明的是以上類型的屬性,有人可能傳入一個(gè)對(duì)應(yīng)的可變版本的實(shí)例吼过,然后在你沒有注意的情況下修改它
符合規(guī)則的:
@property (copy, nonatomic) NSString *variable;
不符合規(guī)則的:
@property (nonatomic) NSString *variable;
關(guān)于代碼段中其他情況的空行锐秦,super 的方法可以在方法中的任意位置調(diào)用,所以 super 方法調(diào)用的代碼前后不需要空行盗忱,原則上除相應(yīng)語法代碼段的空行外在方法中不出現(xiàn)其他空行酱床,如果兩個(gè)代碼段之間需要空行區(qū)分,考慮是否應(yīng)分成兩個(gè)新方法趟佃。如果真的有需要空行區(qū)分扇谣,可以加一行簡(jiǎn)單注釋分隔慷垮,不空行。
-
不使用宏來定義常量揍堕,常量是容易重復(fù)被使用且無需通過查找和代替就能快速修改值料身。常量應(yīng)該使用 const 來聲明而不是使用#define。
符合規(guī)則的:
NSString *const kSRString = @"string"; static CGFloat const kStaticFloat = 50.0;
不符合規(guī)則的:
#define kSRString @"string" #define kSRStaticFloat 50.0
-
文件內(nèi)部使用的字符串常量
static NSString *const kFoo = @"Foo";
供多個(gè)模塊使用的公共字符串常量衩茸,名稱要加項(xiàng)目縮寫前綴
// .m 文件中 NSString *const Foo = @"Foo"; // .h 文件中 extern NSString *const Foo;
常量名以小寫字母 k 開頭是參照匈牙利命名法(Hungarian Notation)芹血,表示常量 constant 的意思。內(nèi)部常量使用這一規(guī)則楞慈,以便和變量名進(jìn)行區(qū)分幔烛。UIKit 不使用這一規(guī)范,直接以縮寫前綴開始囊蓝。所以公共常量直接以縮寫前綴開始饿悬。
-
當(dāng)定義枚舉類型時(shí),使用新的固定基本類型規(guī)格聚霜,因?yàn)樗懈鼜?qiáng)的類型檢查和代碼補(bǔ)全〗铺瘢現(xiàn)在SDK有一個(gè)宏 NS_ENUM() 來幫助和鼓勵(lì)你使用固定的基本類型。
符合規(guī)則的:
typedef NS_ENUM(NSInteger, SREnumType) { SREnumTypeA, SREnumTypeB, SREnumTypeC };
不符合規(guī)則的:
enum SREnumType { SREnumTypeA, SREnumTypeB, SREnumTypeC };
-
當(dāng)在 case 語句塊中聲明變量蝎宇,則該 case 語句塊必須被大括號(hào)包圍弟劲,若一個(gè)
case 語句塊中沒有聲明變量,則不加大括號(hào)姥芥。當(dāng)switch枚舉所有類型時(shí)兔乞,'default'是不需要的符合規(guī)則的:
switch(SREnumType) { case SREnumTypeA: case SREnumTypeB: { NSString *emptyString = @""; // code } break; case SREnumTypeC: // code break; }
-
私有屬性應(yīng)該在類的實(shí)現(xiàn)文件中的類擴(kuò)展(匿名分類)中聲明。
符合規(guī)則的:
@interface SRViewController () @property (nonatomic) GADBannerView *googleAdView; @property (nonatomic) ADBannerView *iAdView; @property (nonatomic) UIWebView *adXWebView; @end
-
Objective-C 使用 YES 和 NO凉唐。因?yàn)?true 和 false 應(yīng)該只在
CoreFoundation庸追,C 或 C++ 代碼使用。既然 nil 解析成 NO台囱,所以沒有必要在條件語句比較淡溯。不要拿某樣?xùn)|西直接與 YES 比較,因?yàn)閅ES被定義為1而一個(gè) BOOL
能被設(shè)置為8位玄坦。符合規(guī)則的:
if (someObject) { } if (![anotherObject boolValue]) { }
不符合規(guī)則的:
if (someObject == nil) {} if ([anotherObject boolValue] == NO) {} if (isAwesome == YES) {} // Never do this. if (isAwesome == true) {} // Never do this.
-
條件語句主體必須使用大括號(hào)包圍血筑,即使只有一行代碼
符合規(guī)則的:
if (!error) { return success; }
不符合規(guī)則的:
if (!error) return success; //or if (!error) return success;
-
當(dāng)需要提高代碼的清晰性和簡(jiǎn)潔性時(shí),三元操作符
?:
才會(huì)使用煎楣。單個(gè)條件求值常常需要它豺总。多個(gè)條件求值時(shí),如果使用if語句或重構(gòu)成實(shí)例變量時(shí)择懂,代碼會(huì)更加易讀喻喳。一般來說,最好使用三元操作符是在根據(jù)條件來賦值的情況下困曙。Non-boolean的變量與某東西比較表伦,加上括號(hào)()會(huì)提高可讀性谦去。如果被比較的變量是boolean類型,那么就不需要括號(hào)蹦哼。符合規(guī)則的:
NSInteger value = 5; result = (value != 0) ? x : y; BOOL isHorizontal = YES; result = isHorizontal ? x : y;
不符合規(guī)則的:
result = a > b ? x = c > d ? c : d : y;
-
Init 方法應(yīng)該遵循 Apple 生成代碼模板的命名規(guī)則鳄哭,返回類型應(yīng)該使用 instancetype 而不是 id。
符合規(guī)則的:
- (instancetype)init { self = [super init]; if (self) { // ... } return self; }
-
當(dāng)類構(gòu)造方法被使用時(shí)纲熏,它應(yīng)該返回類型是 instancetype 而不是
id妆丘。這樣確保編譯器正確地推斷結(jié)果類型。符合規(guī)則的:
@interface Airplane + (instancetype)airplaneWithType:(RWTAirplaneType)type; @end
-
當(dāng)訪問 CGRect 里的 x局劲、y、width 或 height 時(shí)鱼填,應(yīng)該使用CGGeometry 函數(shù)而不是直接通過結(jié)構(gòu)體來訪問药有。引用Apple的CGGeometry:
在這個(gè)參考文檔中所有的函數(shù)羊苟,接受CGRect結(jié)構(gòu)體作為輸入,在計(jì)算它們結(jié)果時(shí)隱式地標(biāo)準(zhǔn)化這些rectangles扮碧。因此咱旱,你的應(yīng)用程序應(yīng)該避免直接訪問和修改保存在CGRect數(shù)據(jù)結(jié)構(gòu)中的數(shù)據(jù)鲜侥。相反崎苗,使用這些函數(shù)來操縱rectangles和獲取它們的特性。
符合規(guī)則的:
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);
不符合規(guī)則的:
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 };
-
當(dāng)使用條件語句編碼時(shí),左手邊的代碼應(yīng)該是"golden" 或 "happy"路徑。也就是不要嵌套if語句宿刮,多個(gè)返回語句也是OK。
符合規(guī)則的:
- (void)someMethod { if (![someOther boolValue]) { return; } //Do something important }
不符合規(guī)則的:
- (void)someMethod { if ([someOther boolValue]) { //Do something important } }
-
當(dāng)方法通過引用來返回一個(gè)錯(cuò)誤參數(shù)脚囊,判斷返回值而不是錯(cuò)誤變量。在成功的情況下桐磁,有些Apple的APIs記錄垃圾值(garbage values)到錯(cuò)誤參數(shù)(如果non-NULL)悔耘,那么判斷錯(cuò)誤值會(huì)導(dǎo)致false負(fù)值和crash。
符合規(guī)則的:
NSError *error; if (![self trySomethingWithError:&error]) { // Handle Error }
不符合規(guī)則的:
NSError *error; [self trySomethingWithError:&error]; if (error) { // Handle Error }
-
單例對(duì)象應(yīng)該使用線程安全模式來創(chuàng)建共享實(shí)例我擂。
符合規(guī)則的:
+ (instancetype)sharedInstance { static id sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }
不符合規(guī)則的:
+ (instancetype)sharedInstance { static MyClass *shared = nil; if(shared == nil) { shared = [[MyClass alloc] init]; } return shared; }
物理文件目錄應(yīng)該與 Xcode 工程文件目錄保持同步來避免文件擴(kuò)張衬以。任何Xcode分組的創(chuàng)建應(yīng)該在文件系統(tǒng)的文件體現(xiàn)。代碼不僅是根據(jù)類型來分組校摩,而且還可以根據(jù)功能來分組看峻,這樣代碼更加清晰。
-
除了使用帶參數(shù)的init方法初始化實(shí)例時(shí)與alloc方法鏈?zhǔn)秸{(diào)用衙吩,其他情況一般不鏈?zhǔn)秸{(diào)用互妓。
符合規(guī)則的:
MyClass *myClass = [[MyClass alloc] initWithName:@""]; NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; [notificationCenter addObserver:self selector:@selector(disposeNotification:) name:SRNotification object:nil];
不符合規(guī)則的:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(disposeNotification:) name:SRNotification object:nil];
-
當(dāng)變量不使用默認(rèn)的 strong 進(jìn)行修飾時(shí),需明確寫出指定的修飾符
符合規(guī)則的:
MyClass * __weak myWeakReference; MyClass * __unsafe_unretained myUnsafeReference;
不符合規(guī)則的:
__weak MyClass *myWeakReference; __unsafe_unretained MyClass *myUnsafeReference;