這份規(guī)范指南概括了本 iOS 團(tuán)隊(duì)的代碼約定。
目錄
- 源文件排版
- 命名
- 常量癌蚁,全局量借杰,靜態(tài)量
- 代碼的文檔化
- Objective-C最佳實(shí)踐
- Xcode工程
源文件排版
1.縮進(jìn)
- 一個(gè)縮進(jìn)使用4個(gè)空格,不要使用制表符(tab)進(jìn)行縮進(jìn)吏砂。請確保在Xcode中正確設(shè)置蝇恶。
2.空格
- 關(guān)鍵字(
if
/else
/for
/switch
/while
等等)與后面的內(nèi)容要加空格拳魁。 - 雙目運(yùn)算符,比較運(yùn)算符撮弧,邏輯運(yùn)算符與左潘懊,右操作數(shù)之間要加空格。
- for循環(huán)中贿衍,
;
之后要加空格授舟。 - 在方法簽名中,在 -/+ 符號后應(yīng)該有一個(gè)空格贸辈。
- @property和其后的括號之間要加一個(gè)空格释树。
- @property之后的括號中,每個(gè)attribute之后的逗號之后加一個(gè)空格擎淤。
-
<>
中的多個(gè)協(xié)議之間奢啥,要加一個(gè)空格。
3.大括號
- 方法的大括號和其他的大括號(
if
/else
/for
/switch
/while
等等)始終和聲明在同一行開始嘴拢,在新的一行結(jié)束桩盲。 -
if
/else
/for
/switch
/while
等等,應(yīng)始終在其后使用大括號席吴,即使其后的代碼塊只有一行代碼赌结。
4.空行和換行
-
else
應(yīng)單獨(dú)占一行,其左邊不應(yīng)再出現(xiàn)右大括號抢腐。 -
@interface
,@implementation
,@protocol
之后姑曙,@end
之前襟交,都要有空行迈倍。 - 方法之間要加空行。
- 使用空行來分隔方法中的功能塊捣域,可以提高代碼的可讀性啼染。
-
@synthesize
和@dynamic
在實(shí)現(xiàn)中每個(gè)都應(yīng)該占一個(gè)新行宴合。 - 每個(gè)源文件,不管是頭文件迹鹅,還是實(shí)現(xiàn)文件卦洽,以空行結(jié)束。
5.導(dǎo)入
如果有一個(gè)以上的 import 語句斜棚,就對這些語句進(jìn)行分組阀蒂。每個(gè)分組的注釋是可選的。
注:對于模塊使用@import
語法弟蚀。
// Frameworks
@import QuartzCore;
// Models
#import "NYTUser.h"
// Views
#import "NYTButton.h"
#import "NYTUserView.h"
6.將代碼分組
- 在頭文件和實(shí)現(xiàn)文件中蚤霞,都可以將屬性,方法進(jìn)行合理的分組义钉。屬于某功能或邏輯的代碼放在一起昧绣,便于閱讀和維護(hù)。
-
dealloc
方法應(yīng)該放在實(shí)現(xiàn)文件的最上面捶闸,并且剛好在@synthesize
和@dynamic
語句的后面夜畴。在任何類中,init
都應(yīng)該直接放在dealloc
方法的下面删壮。 - 在每一組代碼的開頭處贪绘,寫上
#pragma mark - xxx
,其中'xxx'用于解釋這組代碼的用途央碟。
推薦:
int x = 0;
x += 1;
if (x < 2 && y < 3) {
}
for (int k = 0; k < 10; k++) {
}
- (void)setExampleText:(NSString *)text image:(UIImage *)image;
if (user.isHappy) {
// Do something
}
else {
// Do something else
}
@interface NYTSection: NSObject<UITableViewDataSource, UITableViewDelegate>
@property (copy, nonatomic) NSString *headline;
@end
命名
1.語言
- 不要使用漢語拼音來命名
- 使用英國英語兔簇,例如:使用color,而不是colour
- 不能出現(xiàn)單詞拼寫錯(cuò)誤
2.前綴
- 選擇一個(gè)與公司硬耍,應(yīng)用或者二者均有關(guān)聯(lián)的名稱作為命名時(shí)使用的前綴垄琐。
- 前綴最好是三個(gè)字母,因?yàn)樘O果保留了使用所有的兩字母前綴的權(quán)利经柴。
- 類名狸窘,協(xié)議名,分類名坯认,常量名翻擒,都要加大寫的前綴
- 自定義分類的方法,要加小寫的前綴和下劃線牛哺。
3.變量(屬性陋气,實(shí)例變量,局部變量)
- 使用駝峰命名法引润,首字母小寫巩趁,之后的每個(gè)單詞的首字母大寫。
- 使用完整的單詞淳附,不要嫌名字過長议慰。只有當(dāng)一個(gè)縮寫是眾所周知的蠢古,才可以使用。
- 單字母的變量名别凹,只應(yīng)當(dāng)出現(xiàn)在
for
循環(huán)這樣的場合草讶。 - 不要在名字前加任何前綴,如
m, p
等炉菲。 - 實(shí)例變量以下劃線開頭堕战。
- 指針類型的*號,要緊跟變量名拍霜,不加空格践啄。例如:
NSString *text;
,不能是NSString* text;
或是NSString * text;
4.方法
- 方法名以小寫字母開頭沉御,之后每個(gè)單詞的首字母大寫屿讽。
- 如果第一個(gè)單詞是TIFF或是PDF這樣的眾所周知的縮寫,則不必小寫字母開頭吠裆。
- 定義在Category中的方法伐谈,要加app或是organization相關(guān)的前綴,避免命名沖突试疙。
- 每個(gè)參數(shù)前的關(guān)鍵字不能省略诵棵,且要正確的描述參數(shù)的用途。
- 不要在關(guān)鍵字之前加and或者with祝旷,只有當(dāng)一個(gè)方法描述了不同的動作時(shí)履澳,才用and來連接。
5.通知
通知的命名格式為:[類名] + [Did | Will] + [描述] + Notification
例子:
UIApplicationDidEnterBackgroundNotification
NSApplicationDidBecomeActiveNotification
NSWindowDidMiniaturizeNotification
NSTextViewDidChangeSelectionNotification
6.異常
異常的命名格式為:[Prefix] + [UniquePartOfName] + Exception
例子:
NSColorListIOException
NSColorListNotEditableException
NSDraggingException
NSFontUnavailableException
NSIllegalSelectorException
常量怀跛,全局量距贷,靜態(tài)量
- 不要在代碼中出現(xiàn)常量的字符串,用于文案的顯示吻谋。
- 不要再使用宏(
#define
)來定義常量忠蝗,使用const,枚舉來替代漓拾。 - 常量名字前加前綴阁最,每個(gè)單詞的首字母都大寫。
- 常量要定義在.m, .c, .cpp文件中骇两,在頭文件中進(jìn)行extern聲明速种。
- 如果常量僅在某個(gè)實(shí)現(xiàn)文件中使用,則在定義的時(shí)候加static關(guān)鍵字低千,可以避免符號重定義的錯(cuò)誤配阵。
- 如果一個(gè)全局量是非const的,則名字前加g。
例子:
.h:
extern NSString * const RWTAboutViewControllerCompanyName;
extern CGFloat const RWTImageThumbnailHeight;
extern BOOL gRWTEnableHardwareEncoding;
.m:
NSString * const RWTAboutViewControllerCompanyName = @"RayWenderlich.com";
CGFloat const RWTImageThumbnailHeight = 50.0;
BOOL gRWTEnableHardwareEncoding = YES;
static NSInteger const RWTMaxNameLength = 20;
代碼的文檔化
參考文章:https://www.raywenderlich.com/66395/documenting-in-xcode-with-headerdoc-tutorial
使用HeaderDoc規(guī)范來寫代碼的文檔闸餐,可以使得我們能夠象查看官方文檔一樣饱亮,查看自己的代碼的文檔矾芙,而不必跳轉(zhuǎn)到對方的頭文件去查看注釋舍沙。
1.選擇注釋符號
- 對于單行的注釋,使用
///
- 對于塊注釋剔宪,使用
/** 一段文字 */
或者
/*! 一段文字 */
2.使用Tags
-
@brief
,@abstract
用于添加簡要的描述信息 -
@discussion
和@brief, @abstract類似拂铡,不過允許多行 -
@param
用于描述函數(shù)參數(shù)的名字,含義 -
@return
用于描述函數(shù)的返回值 -
@typedef
用于說明這是一個(gè)類型定義 -
@enum
用于說明這是一個(gè)枚舉定義 -
@constant
用于為枚舉值添加注釋 -
@field
用于為結(jié)構(gòu)體中的字段添加注釋 -
@code
用于在文檔中添加示例代碼 -
@warning
用于添加警告信息
3.示例
@interface MathAPI : NSObject
/*!
* @discussion A really simple way to calculate the sum of two numbers.
* @param firstNumber An NSInteger to be used in the summation of two numbers
* @param secondNumber The second half of the equation.
* @warning Please make note that this method is only good for adding non-negative numbers.
* @return The sum of the two numbers passed in.
*/
+ (NSInteger)addNumber:(NSInteger)firstNumber toNumber:(NSInteger)secondNumber;
@end
/*!
* @enum CarType
* @brief A list of newer car types.
* @constant CarTypeHatchback
Hatchbacks are fun, but small.
* @constant CarTypeSedan
Sedans should have enough room to put your kids, and your golf clubs
* @constant CarTypeEstate
Estate cars should hold your kids, groceries, sport equipment, etc.
* @constant CarTypeSport
Sport cars should be fast, fun, and hard on the back.
*/
typedef NS_ENUM(NSInteger, CarType){
/// Hatchbacks are fun, but small.
CarTypeHatchback,
/// Sedans should have enough room to put your kids, and your golf clubs
CarTypeSedan,
/// Estate cars should hold your kids, groceries, sport equipment, etc.
CarTypeEstate,
/// Sport cars should be fast, fun, and hard on the back.
CarTypeSport
};
/*!
* @typedef Old Car Types
* @brief A list of older car types.
* @constant OldCarTypeModelT A cool old car.
* @constant OldCarTypeModelA A sophisticated old car.
*/
typedef enum {
OldCarTypeModelT,
OldCarTypeModelA
} OldCarType;
/*!
* @brief A block that makes the car drive.
* @param distance The distance is equal to a distance driven when the block is ready to execute. It could be miles, or kilometers, but not both. Just pick one and stick with it. ;]
*/
typedef void(^driveCompletion)(CGFloat distance);
@interface Car : NSObject
@property (nonatomic) UIColor *exteriorColor;
@property (nonatomic) NSString *nickname;
/// Indicates the kind of car as enumerated in the "CarType" NS_ENUM
@property (nonatomic, assign) CarType carType;
/*!
* @brief The car will drive, and then execute the drive block
* @param completion A driveCompletion block
* @code [car driveCarWithCompletion:^(CGFloat distance){
NSLog(@"Distance driven %f", distance);
}];
*/
- (void)driveCarWithCompletion:(driveCompletion)completion;
@end
Objective-C 最佳實(shí)踐
1.點(diǎn)語法
- 訪問其它類的屬性時(shí)葱绒,使用點(diǎn)語法感帅,而不是普通的加方括號的函數(shù)調(diào)用形式。
- 在自定義類的內(nèi)部實(shí)現(xiàn)中地淀,對于property仍然是盡量使用點(diǎn)語法失球,但以下4個(gè)場合除外:init, dealloc, 自定義的setter,自定義的getter帮毁。在init, dealloc中使用點(diǎn)語法实苞,會有造成副作用的危險(xiǎn)。而在自定義的訪問方法中烈疚,使用點(diǎn)語法會造成無限的遞歸調(diào)用黔牵。
關(guān)于副作用的討論,可以看Stack Overflow上的一篇討論:Initializing a property, dot notation
2.使用模板的寫法
- 在聲明集合類型的變量或?qū)傩詴r(shí)爷肝,如果有可能猾浦,要指出里面的元素的類型。這樣做的好處是灯抛,寫代碼的時(shí)候編輯器會給出代碼提示金赦。并且,如果輸入了對應(yīng)類型不支持的方法对嚼,編譯器會給出警告素邪,有助于及早的發(fā)現(xiàn)錯(cuò)誤。
- 在使用IBOutletCollection時(shí)猪半,Xcode產(chǎn)生的代碼沒有使用模板的寫法兔朦,我們可以手動去修改。
3. init方法或是類的工廠方法中磨确,使用instancetype作為返回類型
目的是為了提高類型安全沽甥,在編譯器就可以發(fā)現(xiàn)錯(cuò)誤的函數(shù)調(diào)用。
4. 使用NS_DESIGNATED_INITIALIZER來標(biāo)識指定的初始化函數(shù)
- 指定的初始化函數(shù)乏奥,擁有最多的參數(shù)摆舟,是最靈活的初始化函數(shù)。
- 其它的初始化函數(shù),被稱為便利初始化函數(shù)恨诱,是為了使用時(shí)方便媳瞪,它們需要的參數(shù)比較少,未提供的參數(shù)使用默認(rèn)值照宝。這些初始化函數(shù)本身并不會去實(shí)現(xiàn)初始化過程蛇受,它們都是簡單的調(diào)用指定的初始化函數(shù)。
5.使用新式語法
構(gòu)建字面量
NSString:@"Hello"
NSNumber: @(100)
NSNumber: @(YES)
NSArray: @[@"1", @"2", @"3"]
NSDictionary: @{@"1" : @"Hello", @"2" : @"World"}使用下標(biāo)運(yùn)算符
對于字典和數(shù)組厕鹃,當(dāng)訪問里面的元素時(shí)兢仰,不再建議調(diào)用[NSArray objectAtIndex:]或[NSDictionary objectForKey:],而是應(yīng)該使用更簡潔剂碴,可讀性更好的下標(biāo)運(yùn)算符把将。
6.類的屬性聲明
-
@property
之后的括號中,要顯式的寫出所有需要的attribute忆矛,特別是不能省略內(nèi)存管理的attribute察蹲,不要讓編譯器去使用默認(rèn)值。 - 聲明attribute時(shí)的順序:內(nèi)存管理催训,原子性洽议,其它。這樣可以和Xcode為IBOutlet產(chǎn)生的代碼保持一致瞳腌。
- 如果一個(gè)屬性不想被外部寫入绞铃,那么應(yīng)該在頭文件中使用readonly關(guān)鍵字。如果在類實(shí)現(xiàn)中需要讀寫該屬性的值嫂侍,那么可以重新聲明該屬性儿捧,并去掉readonly關(guān)鍵字,或?qū)⒅臑閞eadwrite關(guān)鍵字挑宠。
- 不必再寫
@synthesize
菲盾,除非少了它就不能編譯通過。如果一個(gè)類所遵從的協(xié)議中聲明了屬性各淀,那么該類需要使用@synthesize
懒鉴。 - 對于NSString, NSArray, NSDictionary類型的屬性,建議使用copy關(guān)鍵字碎浇。如果使用strong临谱,然后將一個(gè)可變類型的對象賦值給該屬性,那么在賦值以后奴璃,該對象可能會被改變悉默,從而造成一些運(yùn)行期的bug出現(xiàn)。
- 不需要暴露給外部的屬性苟穆,將其在類的擴(kuò)展里聲明抄课。
- 如果大于1個(gè)的同類型的IBOutlet唱星,可以被放在一個(gè)數(shù)組里,那么不要聲明單個(gè)的IBOutlet跟磨,而是聲明IBOutletCollection類型的屬性间聊。
7.枚舉和位域的定義
- 定義枚舉,使用NS_ENUM抵拘,并遵循蘋果的命名規(guī)范哎榴。例如:
typedef NS_ENUM(NSInteger, UIViewAnimationCurve) {
UIViewAnimationCurveEaseInOut, // slow at beginning and end
UIViewAnimationCurveEaseIn, // slow at beginning
UIViewAnimationCurveEaseOut, // slow at end
UIViewAnimationCurveLinear,
};
- 定義位域,使用NS_OPTIONS仑濒,命名規(guī)范同枚舉叹话。例如:
typedef NS_OPTIONS(NSUInteger, UIViewAutoresizing) {
UIViewAutoresizingNone = 0,
UIViewAutoresizingFlexibleLeftMargin = 1 << 0,
UIViewAutoresizingFlexibleWidth = 1 << 1,
UIViewAutoresizingFlexibleRightMargin = 1 << 2,
UIViewAutoresizingFlexibleTopMargin = 1 << 3,
UIViewAutoresizingFlexibleHeight = 1 << 4,
UIViewAutoresizingFlexibleBottomMargin = 1 << 5
};
8.使用nonnull, nullable來修飾函數(shù)的參數(shù)或是類的屬性
加上此類修飾符偷遗,可以提高代碼的可讀性墩瞳,調(diào)用者不需要查看源代碼,就可以明白設(shè)計(jì)者的意圖氏豌,并據(jù)此傳遞正確的參數(shù)喉酌。
如果對一個(gè)nonnull修飾的參數(shù)或是屬性,傳入一個(gè)nil參數(shù)泵喘,那么在輸入代碼以后泪电,IDE就可以給出警告,這樣就可以第一時(shí)間發(fā)現(xiàn)問題纪铺。
9.訪問數(shù)組元素時(shí)要小心出現(xiàn)下標(biāo)越界的錯(cuò)誤
- 取數(shù)組元素時(shí)相速,為安全起見,總是要判斷下標(biāo)是否越界鲜锚。
- 如果是取第一個(gè)突诬,或是最后一個(gè)元素,那么可以調(diào)用
firstObject
或'lastObject
方法芜繁,這兩個(gè)方法是安全的旺隙。
10.檢查參數(shù)是否為nil的幾種情況
- 針對nil調(diào)用方法,我們都知道什么也不會發(fā)生骏令,很安全蔬捷,但其它情況下使用nil就未必了。
- 往數(shù)組榔袋,字典中添加nil對象時(shí)周拐,會導(dǎo)致崩潰。因此凰兑,最好總是檢查參數(shù)的值是否有效妥粟,除非非常確信是有效的。
- 使用
NSJsonSerialization
的JSONObjectWithData:options:error:
方法時(shí)聪黎,如果data參數(shù)為nil罕容,也會導(dǎo)致崩潰备恤,所以這里也需要檢查。
11.單例
單例對象應(yīng)該使用線程安全的模式創(chuàng)建共享的實(shí)例锦秒。
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
12.使用CGRect函數(shù)
當(dāng)訪問一個(gè) CGRect
的 x
露泊, y
, width
旅择, height
時(shí)惭笑,應(yīng)該使用CGRect相關(guān)的函數(shù)代替直接訪問結(jié)構(gòu)體成員。蘋果的 CGGeometry
參考中說到:
All functions described in this reference that take CGRect data structures as inputs implicitly standardize those rectangles before calculating their results. For this reason, your applications should avoid directly reading and writing the data stored in the CGRect data structure. Instead, use the functions described here to manipulate rectangles and to retrieve their characteristics.
推薦:
CGRect frame = self.view.frame;
CGFloat x = CGRectGetMinX(frame);
CGFloat y = CGRectGetMinY(frame);
CGFloat width = CGRectGetWidth(frame);
CGFloat height = CGRectGetHeight(frame);
反對:
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;
13.錯(cuò)誤處理
當(dāng)引用一個(gè)返回錯(cuò)誤參數(shù)(error parameter)的方法時(shí),應(yīng)該針對返回值潦匈,而非錯(cuò)誤變量揭糕。
推薦:
NSError *error;
if (![self trySomethingWithError:&error]) {
// 處理錯(cuò)誤
}
反對:
NSError *error;
[self trySomethingWithError:&error];
if (error) {
// 處理錯(cuò)誤
}
14.頭文件中要盡可能少的import其它的頭文件
- 在聲明中用到其它類類型時(shí),優(yōu)先使用前向聲明川蒙,除非使用前向聲明不能通過編譯。
- 將
#import
語句盡可能的轉(zhuǎn)移到.m文件中长已。
這樣做可以減少文件間的編譯依賴畜眨,特別是某個(gè)文件修改時(shí),能減少需要編譯的文件的數(shù)量术瓮,加快編譯速度康聂。
Xcode 工程
- 若無特殊需要,嚴(yán)禁在Project Navigator中使用Group功能胞四,Navigator中的目錄結(jié)構(gòu)恬汁,應(yīng)該和文件系統(tǒng)中的實(shí)際目錄嚴(yán)格對應(yīng)。
- Build時(shí)不能產(chǎn)生警告辜伟。有些警告如果自己確定沒有問題氓侧,可以想辦法消除掉。例如針對performSelector的警告可以這樣消除:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self performSelector:sel withObject:arguments];
#pragma clang diagnostic pop
- 圖片資源游昼,需要提供所有的scale版本甘苍,且應(yīng)該是嚴(yán)格的1x, 2x, 3x的關(guān)系。這是因?yàn)槿绻麍D片的大小和視圖的大小不一致烘豌,會產(chǎn)生像素不對齊的問題载庭,傷性能。
- 除非特殊情況(如做本地化)廊佩,圖片資源都應(yīng)該使用Assets進(jìn)行管理囚聚,這樣做的好處有兩個(gè):1)下載后的安裝包里,只會包含一套@2x或是@3x的資源标锄,而不會2套資源都包含顽铸。這屬于蘋果在應(yīng)用瘦身方面做的一項(xiàng)優(yōu)化。2)相比文件夾方式料皇,加載速度快谓松。
- 圖片資源應(yīng)該根據(jù)使用場景進(jìn)行合理的目錄劃分星压,但在Assets中最好避免圖片很多的目錄,因?yàn)辄c(diǎn)擊一個(gè)大目錄時(shí)鬼譬,Xcode有時(shí)容易無響應(yīng)娜膘。
- 圖片資源不要用中文命名,不要用拼音命名优质,也不要進(jìn)行簡單的命名竣贪。正確的命名格式如:profile_follow_button_normal.png, profile_follow_button_pressed.png, loading_animation_1.png。合理的命名巩螃,有助于找出工程中的垃圾資源演怎。
- 程序運(yùn)行時(shí),不能有布局錯(cuò)誤的警告避乏。
- 程序最好能同時(shí)在模擬器和實(shí)際設(shè)備上運(yùn)行爷耀。在模擬器上運(yùn)行的好處:1)方便測試屏幕適配 2)檢查一些性能問題,如像素不對齊淑际,離屏渲染等畏纲。