天天看點

objective-c 編寫規範_Objective-C 程式設計規範

命名規範

1.【強制】 代碼中的命名均不能以下劃線或美元符号開始,也不能以下劃線或美元符号結束。

反例: _name / __name / $Object / name_ / name$ / Object$

2.【強制】 代碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式。

說明:正确的英文拼寫和文法可以讓閱讀者易于了解,避免歧義。注意,即使純拼音命名方式 也要避免采用。

反例:DaZhePromotion [打折] / getPingfenByName() [評分] / int 某變量 = 3

正例:alibaba / taobao / youku / hangzhou 等國際通用的名稱,可視同英文。

3.【強制】類名使用 UpperCamelCase 風格,必須遵從駝峰形式,但領域模型相關的命名除外,比如 UserDAO

正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion

反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion

4.【強制】方法名、參數名、成員變量、局部變量都統一使用 lowerCamelCase 風格,必須遵從 駝峰形式。

正例:localValue / getHttpMessage / inputUserId

5.【強制】常量命名全部大寫,單詞間用下劃線隔開,力求語義表達完整清楚,不要嫌名字長。

正例:MAX_STOCK_COUNT

反例:MAX_COUNT

6.【強制】異常類命名使用 Exception 結尾;測試類命名以它要測試的類的名稱開始,以 Test 結尾。

7.【強制】杜絕完全不規範的縮寫,避免望文不知義。

8.【強制】檔案名和類名同名。

9.【推薦】如果使用到了設計模式,建議在類名中展現出具體模式。

說明:将設計模式展現在名字中,有利于閱讀者快速了解架構設計思想。

10.【推薦】類名應加上三個大寫字母作為字首(兩個字母的為 Apple 的類保留)。

11.【強制】特殊類的命名,如果是視圖控制器的子類應添加字尾“ViewController”或者“Controller”,如果是視圖的子類應添加字尾“View”,如果是按鈕的子類應添加字尾“Button”。

12.【強制】分類 Category 命名,與類命名相同,此外需添加要擴充的類名和“+”

13.【強制】協定(委托)命名,與類命名相同,此外需添加“Delegate”字尾

14.【推薦】方法傳回值為Boolean 類型,應加 is 字首。如果某方法傳回非屬性的 Boolean 值,那麼應該根據其功能,選用 has 或 is 當字首。

- (BOOL)isEqualToString:(NSString *)aString;

- (BOOL)hasPrefix:(NSString *)aString;

15.【推薦】方法名不要使用 new 作為字首

16.【推薦】不要使用 and 來連接配接用屬性作參數的關鍵字

17.【推薦】方法連接配接多個參數命名時,适當使用合适的介詞。比如 in for at by of with

//清晰

insertObject:atIndex:

//不清晰,insert的對象類型和at的位置屬性沒有說明

insert:at:

//清晰

removeObjectAtIndex:

//不清晰,remove的對象類型沒有說明,參數的作用沒有說明

remove:

18.【推薦】定義成員變量,添加下劃線字首。但不推薦使用成員變量,直接使用屬性。

19.【強制】資源圖檔命名,圖檔應該與類檔案一樣,按子產品分組放置,縮略字首可用如下例子

icon btn bg line logo pic img

常量定義

1.【強制】不允許出現任何魔法值(即未經定義的常量)直接出現在代碼中。

2.【推薦】不要使用一個常量類維護所有常量,應該按常量功能進行歸類,分開維護。

如:緩存相關的常量放在類:CacheConsts下;系統配置相關的常量放在類:ConfigConsts下。

說明:大而全的常量類,非得使用查找功能才能定位到修改的常量,不利于了解和維護。

3.【強制】常量使用小寫k開頭的駝峰法。

static const NSTimeInterval kAnimationDuration = 0.3;

4.【強制】若常量作用域超出編譯單元,在類外可見時,

.h

extern NSString *const EOCStringConstant;

.m

NSString *const EOCStringConstant = @"aaa";

5.【強制】全局常量(通知或者關鍵字等)盡量用const來定義,而不是宏。因為如果使用宏定義, 宏可能被重定義,引用不同的檔案可能會導緻宏的不同。

格式規範

1.【強制】大括号的使用約定。如果是大括号内為空,則簡潔地寫成{}即可,不需要換行;如果 是非空代碼塊則:

左大括号前不換行。

左大括号後換行。

右大括号前換行。

右大括号後還有else等代碼則不換行;表示終止右大括号後必須換行。

2.【強制】 左括号和後一個字元之間不出現空格;同樣,右括号和前一個字元之間也不出現空格。

3.【強制】if/for/while/switch/do 等保留字與左右括号之間都必須加空格。

4.【強制】任何運算符左右必須加一個空格。

說明:運算符包括指派運算符=、邏輯運算符&&、加減乘除符号、三目運算符等。

5.【強制】縮進采用 4 個空格,禁止使用 tab 字元。

- (void)method {

// 縮進 4 個空格

NSString *say = @"hello";

// 運算符的左右必須有一個空格

NSInteger flag = 0;

// 關鍵詞 if 與括号之間必須有一個空格,括号内的 f 與左括号,0 與右括号不需要空格

if (flag == 0) {

NSLog(@"%@", say);

}

// 左大括号前不加空格且不換行,左大括号後換行

if (flag == 1) {

NSLog(@"world");

// 右大括号前換行,右大括号後有 else,不用換行

} else {

NSLog(@"ok");

// 在右大括号後直接結束,則必須換行

}

}

6.【強制】單行字元數限制不超過 120 個,超出需要換行,按照:來對齊分行顯示

-(id)initWithModel:(IPCModle)model

ConnectType:(IPCConnectType)connectType

Resolution:(IPCResolution)resolution

AuthName:(NSString *)authName

Password:(NSString *)password

MAC:(NSString *)mac

AzIp:(NSString *)az_ip

AzDns:(NSString *)az_dns

Token:(NSString *)token

Email:(NSString *)email

Delegate:(id)delegate;

在分行時,如果第一段名稱過短,後續名稱可以以Tab的長度(4個空格)為機關進行縮進:

- (void)short:(GTMFoo *)theFoo

longKeyword:(NSRect)theRect

evenLongerKeyword:(float)theInterval

error:(NSError **)theError {

...

}

函數的調用同理。

//寫在一行

[myObject doFooWith:arg1 name:arg2 error:arg3];

//分行寫,按照':'對齊

[myObject doFooWith:arg1

name:arg2

error:arg3];

//第一段名稱過短的話後續可以進行縮進

[myObj short:arg1

longKeyword:arg2

evenLongerKeyword:arg3

error:arg4];

7.【強制】多個參數逗号後邊必須加空格。

正例:

NSLog(@"%@-%@-%@-%@", a, b, c, d);

8.【推薦】沒有必要增加若幹空格來使某一行的字元與上一行的相應字元對齊。

說明:在變量比較多的 情況下,是一種累贅的事情。

@interface HOECoupon : NSObject

@property (nonatomic, assign) NSInteger couponId;

@property (nonatomic, copy) NSString *startTime;

@property (nonatomic, copy) NSString *endTime;

@property (nonatomic, copy) NSString *title;

@property (nonatomic, copy) NSString *subTitle;

@property (nonatomic, assign) NSInteger state;

@property (nonatomic, assign) NSInteger canUse;

@property (nonatomic, assign) CGFloat price;

@property (nonatomic, copy) NSString *stateDesc;

@property (nonatomic, copy) NSString *reason;

@end

9.【推薦】方法體内的執行語句組、變量的定義語句組、不同的業務邏輯之間或者不同的語義之間插入一個空行。相同業務邏輯和語義之間不需要插入空行。

說明:沒有必要插入多行空格進行隔開。

10.【推薦】.m實作類中推薦的代碼結構如下

#pragma mark - lifecycle

- (void)dealloc

- (void)viewDidLoad

- (void)viewWillAppear:(BOOL)animated

...

#pragma mark - UITableViewDataSource & UITableViewDelegate

...

#pragma mark - Event handlers

- (void)didTapLikeButton:

...

#pragma mark - public methods

...

#pragma mark - private methods

- (void)setupTableView

- (void)setupNotification

...

#pragma mark - getters & setters

...

11.【推薦】頭檔案引用的順序

// controllers

#import "OrcaAddFriendFromContactsViewController.h"

...

// views

...

// consts

...

// utils

...

// others

12.【推薦】方法的格式,在-和(void)之間應該有一個空格

- (void)writeVideoFrameWithData:(NSData *)frameData timeStamp:(int)timeStamp {

...

}

面向對象規範

1.【強制】避免通過一個類的對象引用通路此類的靜态變量或靜态方法,無謂增加編譯器解析成本,直接用類名來通路即可。

2.【強制】不能使用過時的類或方法。

3.【強制】推薦使用 NS_ENUM ,而不是 enum

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {

UIViewAnimationTransitionNone,

UIViewAnimationTransitionFlipFromLeft,

UIViewAnimationTransitionFlipFromRight,

UIViewAnimationTransitionCurlUp,

UIViewAnimationTransitionCurlDown,

};

4.【推薦】盡量使用字面量來初始化

NSArray *names = @[@"Brian", @"Matt", @"Chris", @"Alex", @"Steve"];

NSDictionary *productManagers = @{@"iPhone" : @"Kate", @"iPad" : @"Kamal"};

NSNumber *shouldUseLiterals = @YES;

NSNumber *buildingZIPCode = @10018;

5.【推薦】建議定義屬性的時候把所有的參數寫全, 尤其是如果想定義成隻讀的(防止外面修改)那一定要加上readonly, 這也是代碼安全性的一個習慣。

@property (nonatomic, readwrite, copy) NSString *name;

6.【強制】方法參數過多,需要進行重構

正例:

- (void)registerUser(User *user) {

// to do...

}

反例:

- (void)registerUserName:(NSString *)userName

password:(NSString *)password

email:(NSString *)email {

// to do...

}

7.【強制】不推薦使用成員變量,直接使用屬性。

8.【推薦】setter 方法中,參數名稱與類成員變量名稱一緻,self.成員名 = 參數名。在getter/setter 方法中,盡量不要增加業務邏輯,增加排查問題的難度。

9.【推薦】對于簡單對象,推薦在getter方法中延遲加載屬性,如果建立複雜對象,那麼對象的建立最好放到類的初始化時,而不是延遲加載。

10.【推薦】協定,在書寫協定的時候注意用<>括起來的協定和類型名之間是沒有空格的

@interface MyProtocoledClass : NSObject {

}

- (void)setDelegate:(id)aDelegate;

@end

11.【推薦】閉包Block規則

較短的block可以寫在一行内。

如果分行顯示的話,block的右括号}應該和調用block那行代碼的第一個非空字元對齊。

block内的代碼采用4個空格的縮進。

如果block過于龐大,應該單獨聲明成一個變量來使用。

^ 和(之間,^ 和{之間都沒有空格,參數清單的右括号)和{之間有一個空格。

//較短的block寫在一行内

[operation setCompletionBlock:^{ [self onOperationDone]; }];

//分行書寫的block,内部使用4空格縮進

[operation setCompletionBlock:^{

[self.delegate newDataAvailable];

}];

//使用C語言API調用的block遵循同樣的書寫規則

dispatch_async(_fileIOQueue, ^{

NSString* path = [self sessionFilePath];

if (path) {

// ...

}

});

//較長的block關鍵字可以縮進後在新行書寫,注意block的右括号'}'和調用block那行代碼的第一個非空字元對齊

[[SessionService sharedService]

loadWindowWithCompletionBlock:^(SessionWindow *window) {

if (window) {

[self windowDidLoad:window];

} else {

[self errorLoadingWindow];

}

}];

//較長的block參數清單同樣可以縮進後在新行書寫

[[SessionService sharedService]

loadWindowWithCompletionBlock:

^(SessionWindow *window) {

if (window) {

[self windowDidLoad:window];

} else {

[self errorLoadingWindow];

}

}];

//龐大的block應該單獨定義成變量使用

void (^largeBlock)(void) = ^{

// ...

};

[_operationQueue addOperationWithBlock:largeBlock];

//在一個調用中使用多個block,注意到他們不是像函數那樣通過':'對齊的,而是同時進行了4個空格的縮進

[myObject doSomethingWith:arg1

firstBlock:^(Foo *a) {

// ...

}

secondBlock:^(Bar *b) {

// ...

}];

12.【推薦】不要用點分文法來調用方法,隻用來通路屬性。這樣是為了防止代碼可讀性問題。

//正确,使用點分文法通路屬性

NSString *oldName = myObject.name;

myObject.name = @"Alice";

//錯誤,不要用點分文法調用方法

NSArray *array = [NSArray arrayWithObject:@"hello"];

NSUInteger numberOfItems = array.count;

13.BOOL 的使用

BOOL在Objective-C中被定義為signed char類型,這意味着一個BOOL類型的變量不僅僅可以表示YES(1)和NO(0)兩個值,是以永遠不要将BOOL類型變量直接和YES比較:

//錯誤,無法确定|great|的值是否是YES(1),不要将BOOL值直接與YES比較

BOOL great = [foo isGreat];

if (great == YES)

// ...be great!

//正确

BOOL great = [foo isGreat];

if (great)

// ...be great!

同樣的,也不要将其它類型的值作為BOOL來傳回,這種情況下,BOOL變量隻會取值的最後一個位元組來指派,這樣很可能會取到0(NO)。但是,一些邏輯操作符比如&&,||,!的傳回是可以直接賦給BOOL的:

//錯誤,不要将其它類型轉化為BOOL傳回

- (BOOL)isBold {

return [self fontTraits] & NSFontBoldTrait;

}

- (BOOL)isValid {

return [self stringValue];

}

//正确

- (BOOL)isBold {

return ([self fontTraits] & NSFontBoldTrait) ? YES : NO;

}

//正确,邏輯操作符可以直接轉化為BOOL

- (BOOL)isValid {

return [self stringValue] != nil;

}

- (BOOL)isEnabled {

return [self isValid] && [self isBold];

}

14.不要使用new方法

盡管很多時候能用new代替alloc init方法,但這可能會導緻調試記憶體時出現不可預料的問題。Cocoa的規範就是使用alloc init方法,使用new會讓一些讀者困惑。

15.Public API要盡量簡潔

共有接口要設計的簡潔,滿足核心的功能需求就可以了。不要設計很少會被用到,但是參數極其複雜的API。如果要定義複雜的方法,使用類别或者類擴充。

集合處理

1.【強制】對于可變集合類型作為屬性時使用strong,不可變集合類型使用copy

2.【強制】盡量不要暴露mutable類型的對象在public interface, 建議在.h定義一個Inmutable類型的屬性, 然後在.m的get函數裡面傳回一個内部定義的mutable變量。保證類的不可變性。

3.使用NSNumber的文法糖

使用帶有@符号的文法糖來生成NSNumber對象能使代碼更簡潔:

NSNumber *fortyTwo = @42;

NSNumber *piOverTwo = @(M_PI / 2);

enum {

kMyEnum = 2;

};

NSNumber *myEnum = @(kMyEnum);

4.保證NSString在指派時被複制

NSString非常常用,在它被傳遞或者指派時應當保證是以複制(copy)的方式進行的,這樣可以防止在不知情的情況下String的值被其它對象修改。

- (void)setFoo:(NSString *)aFoo {

_foo = [aFoo copy];

}

5.按照定義的順序釋放資源

在類或者Controller的生命周期結束時,往往需要做一些掃尾工作,比如釋放資源,停止線程等,這些掃尾工作的釋放順序應當與它們的初始化或者定義的順序保持一緻。這樣做是為了友善調試時尋找錯誤,也能防止遺漏。

并發處理

1.【強制】擷取單例對象需要保證線程安全,其中的方法也要保證線程安全。

2.定義一個屬性時,編譯器會自動生成線程安全的存取方法(Atomic),但這樣會大大降低性能,特别是對于那些需要頻繁存取的屬性來說,是極大的浪費。是以如果定義的屬性不需要線程保護,記得手動添加屬性關鍵字nonatomic來取消編譯器的優化。

控制語句

1.【強制】在一個 switch 塊内,每個 case 要麼通過 break/return 等來終止,要麼注釋說明程 序将繼續執行到哪一個 case 為止;在一個 switch 塊内,都必須包含一個 default 語句并且 放在最後,即使它什麼代碼也沒有。

2.【強制】在 if/else/for/while/do 語句中必須使用大括号,即使隻有一行代碼,避免使用 下面的形式:if (condition) statements;

3.【推薦】推薦盡量少用 else, if-else 的方式可以改寫成:

if (condition) {

...

return obj;

}

// 接着寫 else 的業務邏輯代碼;

4.【強制】請勿超過 3 層,超過請使用狀态設計模式。

正例:邏輯上超過 3 層的 if-else 代碼可以使用衛語句,或者狀态模式來實作。

5.【強制】三目運算符的使用

正例:

result = object ? : [self createObject];

反例:

result = object ? object : [self createObject];

6.【強制】對于複雜的條件判斷,要提取一個變量出來

正例:

([self canDeleteJob:job]) { ... }

- (BOOL)canDeleteJob:(Job *)job {

BOOL invalidJobState = job.JobState == JobState.New

|| job.JobState == JobState.Submitted

|| job.JobState == JobState.Expired;

BOOL invalidJob = job.JobTitle && job.JobTitle.length;

return invalidJobState || invalidJob;

}

反例:

if (job.JobState == JobState.New

|| job.JobState == JobState.Submitted

|| job.JobState == JobState.Expired

|| (job.JobTitle && job.JobTitle.length))

{

//....

}

7.【強制】對于嵌套判斷,一旦發現某個條件不符合,立即傳回,條理更清晰

正例:

if (!user.UserName) return NO;

if (!user.Password) return NO;

if (!user.Email) return NO;

return YES;

反例:

Not preferred:

BOOL isValid = NO;

if (user.UserName)

{

if (user.Password)

{

if (user.Email) isValid = YES;

}

}

return isValid;

注釋規範

1.【強制】類、類屬性、類方法的注釋必須使用文檔注釋,不得使用 //xxx 方式。

2.【強制】所有的類都必須添加建立者資訊。

3.【強制】方法内部單行注釋,在被注釋語句上方另起一行,使用//注釋。方法内部多行注釋

使用注釋,注意與代碼對齊。

4.【強制】所有的枚舉類型字段必須要有注釋,說明每個資料項的用途。

5.【推薦】代碼修改的同時,注釋也要進行相應的修改,尤其是參數、傳回值、異常、核心邏輯 等的修改。

6.【參考】注釋掉的代碼盡量要配合說明,而不是簡單的注釋掉。

說明:代碼被注釋掉有兩種可能性:1)後續會恢複此段代碼邏輯。2)永久不用。前者如果沒 有備注資訊,難以知曉注釋動機。後者建議直接删掉(代碼倉庫儲存了曆史代碼)。

7.【參考】好的命名、代碼結構是自解釋的,注釋力求精簡準确、表達到位。避免出現注釋的一個極端:過多過濫的注釋,代碼的邏輯一旦修改,修改注釋是相當大的負擔。

8.【參考】特殊注釋标記,請注明标記人與标記時間。注意及時處理這些标記,通過标記掃描,經常清理此類标記。線上故障有時候就是來源于這些标記處的代碼。

待辦事宜(TODO):( 标記人,标記時間,[預計處理時間]) 表示需要實作,但目前還未實作的功能。

錯誤,不能工作(FIXME):(标記人,标記時間,[預計處理時間])

在注釋中用 FIXME 标記某代碼是錯誤的,而且不能工作,需要及時糾正的情況。

其他

1.【強制】避免 block 的循環引用

__weak typeof(self) weakSelf = self;

myObj.myBlock = ^{

__strong typeof(self) strongSelf = weakSelf;

if (strongSelf) {

[strongSelf doSomething]; // strongSelf != nil

// preemption, strongSelf still not nil

[strongSelf doSomethingElse]; // strongSelf != nil

}

else {

// Probably nothing...

return;

}

};

2.Delegate要使用弱引用

一個類的Delegate對象通常還引用着類本身,這樣很容易造成引用循環的問題,是以類的Delegate屬性要設定為弱引用。

@property (nonatomic, weak) id delegate;

3.nil檢查

因為在Objective-C中向nil對象發送指令是不會抛出異常或者導緻崩潰的,隻是完全的“什麼都不幹”,是以,隻在程式中使用nil來做邏輯上的檢查。

另外,不要使用諸如nil == Object或者Object == nil的形式來判斷。

//正确,直接判斷

if (!objc) {

...

}

//錯誤,不要使用nil == Object的形式

if (nil == objc) {

...

}