iOS 團(tuán)隊(duì)編程規(guī)范
前 言
需求是暫時(shí)的涨享,只有變化才是永恒的,面向變化編程仆百,而不是面向需求編程厕隧。
不要過分追求技巧,降低程序的可讀性俄周。
簡(jiǎn)潔的代碼可以讓bug無處藏身吁讨。要寫出明顯沒有bug的代碼,而不是沒有明顯bug的代碼峦朗。
先把眼前的問題解決掉建丧,解決好,再考慮將來的擴(kuò)展問題波势。
一翎朱、命名規(guī)范
1橄维、統(tǒng)一要求
含義清楚,盡量做到不需要注釋也能了解其作用拴曲,若做不到争舞,就加注釋,使用全稱澈灼,不使用縮寫竞川。
2、類名
大駝峰式命名:每個(gè)單詞的首字母都采用大寫字母
==例:== MFHomePageViewController
3蕉汪、私有變量
私有變量放在 .m 文件中聲明
以 _ 開頭流译,第一個(gè)單詞首字母小寫,后面的單詞的首字母全部大寫者疤。
==例:== NSString *_somePrivateVariable
4福澡、property變量
小駝峰式命名:第一個(gè)單詞以小寫字母開始,后面的單詞的首字母全部大寫
屬性的關(guān)鍵字推薦按照 原子性驹马,讀寫革砸,內(nèi)存管理的順序排列。
Block糯累、NSString屬性應(yīng)該使用copy關(guān)鍵字
禁止使用synthesize關(guān)鍵詞
==例:==
typedef void (^ErrorCodeBlock) (id errorCode,NSString *message);
@property (nonatomic, readwrite, strong) UIView *headerView; //注釋
@property (nonatomic, readwrite, copy) ErrorCodeBlock errorBlock; //將block拷貝到堆中
@property (nonatomic, readwrite, copy) NSString *userName;
5算利、宏和常量命名
對(duì)于宏定義的常量
#define 預(yù)處理定義的常量全部大寫,單詞間用 _ 分隔
宏定義中如果包含表達(dá)式或變量泳姐,表達(dá)式或變量必須用小括號(hào)括起來效拭。
對(duì)于類型常量
對(duì)于局限于某編譯單元(實(shí)現(xiàn)文件)的常量,以字符k開頭胖秒,例如kAnimationDuration缎患,且需要以static const修飾
對(duì)于定義于類頭文件的常量,外部可見阎肝,則以定義該常量所在類的類名開頭挤渔,例如EOCViewClassAnimationDuration, 仿照蘋果風(fēng)格,在頭文件中進(jìn)行extern聲明风题,在實(shí)現(xiàn)文件中定義其值
==例:==
//宏定義的常量
#define ANIMATION_DURATION 0.3
#define MY_MIN(A, B) ((A)>(B)?(B):(A))
//局部類型常量
static const NSTimeInterval kAnimationDuration = 0.3;
//外部可見類型常量
//EOCViewClass.h
extern const NSTimeInterval EOCViewClassAnimationDuration;
extern NSString *const EOCViewClassStringConstant; //字符串類型
//EOCViewClass.m
const NSTimeInterval EOCViewClassAnimationDuration = 0.3;
NSString *const EOCViewClassStringConstant = @"EOCStringConstant";
6判导、Enum
Enum類型的命名與類的命名規(guī)則一致
Enum中枚舉內(nèi)容的命名需要以該Enum類型名稱開頭
NS_ENUM定義通用枚舉,NS_OPTIONS定義位移枚舉
==例:==
typedef NS_ENUM(NSInteger, UIViewAnimationTransition) {
UIViewAnimationTransitionNone,
UIViewAnimationTransitionFlipFromLeft,
UIViewAnimationTransitionFlipFromRight,
UIViewAnimationTransitionCurlUp,
UIViewAnimationTransitionCurlDown,
};
typedef NS_OPTIONS(NSUInteger, UIControlState) {
UIControlStateNormal = 0,
UIControlStateHighlighted = 1
7沛硅、Delegate
用delegate做后綴眼刃,如
用optional修飾可以不實(shí)現(xiàn)的方法,用required修飾必須實(shí)現(xiàn)的方法
當(dāng)你的委托的方法過多, 可以拆分?jǐn)?shù)據(jù)部分和其他邏輯部分, 數(shù)據(jù)部分用dataSource做后綴. 如
使用did和will通知Delegate已經(jīng)發(fā)生的變化或?qū)⒁l(fā)生的變化摇肌。
類的實(shí)例必須為回調(diào)方法的參數(shù)之一
回調(diào)方法的參數(shù)只有類自己的情況擂红,方法名要符合實(shí)際含義
回調(diào)方法存在兩個(gè)以上參數(shù)的情況,以類的名字開頭朦蕴,以表明此方法是屬于哪個(gè)類的
==例:==
@protocol UITableViewDataSource
@required
//回調(diào)方法存在兩個(gè)以上參數(shù)
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section;
@optional
//回調(diào)方法的參數(shù)只有類自己
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView; // Default is 1 if not implemented
@protocol UITableViewDelegate
@optional
//使用`did`和`will`通知`Delegate`
- (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
8篮条、方法
方法名用小駝峰式命名
方法名不要使用new作為前綴
不要使用and來連接屬性參數(shù)弟头,如果方法描述兩種獨(dú)立的行為,使用and來串接它們涉茧。
方法實(shí)現(xiàn)時(shí)赴恨,如果參數(shù)過長(zhǎng),則令每個(gè)參數(shù)占用一行伴栓,以冒號(hào)對(duì)齊伦连。
一般方法不使用前綴命名,私有方法可以使用統(tǒng)一的前綴來分組和辨識(shí)
方法名要與對(duì)應(yīng)的參數(shù)名保持高度一致
表示對(duì)象行為的方法钳垮、執(zhí)行性的方法應(yīng)該以動(dòng)詞開頭
返回性的方法應(yīng)該以返回的內(nèi)容開頭惑淳,但之前不要加get,除非是間接返回一個(gè)或多個(gè)值饺窿。
可以使用情態(tài)動(dòng)詞(動(dòng)詞前面can歧焦、should、will等)進(jìn)一步說明屬性意思肚医,但不要使用do或does,因?yàn)檫@些助動(dòng)詞沒什么實(shí)際意義绢馍。也不要在動(dòng)詞前使用副詞或形容詞修飾
==例:==
//不要使用 and 來連接屬性參數(shù)
- (int)runModalForDirectory:(NSString *)path file:(NSString *)name types:(NSArray *)fileTypes; //推薦
- (int)runModalForDirectory:(NSString *)path andFile:(NSString *)name andTypes:(NSArray *)fileTypes; //反對(duì)
//表示對(duì)象行為的方法、執(zhí)行性的方法
- (void)insertModel:(id)model atIndex:(NSUInteger)atIndex;
- (void)selectTabViewItem:(NSTableViewItem *)tableViewItem
//返回性的方法
- (instancetype)arrayWithArray:(NSArray *)array;
//參數(shù)過長(zhǎng)的情況
- (void)longMethodWith:(NSString *)theFoo
rect:(CGRect)theRect
interval:(CGFloat)theInterval
{
//Implementation
}
//不要加get
- (NSSize) cellSize; //推薦
- (NSSize) getCellSize; //反對(duì)
//使用情態(tài)動(dòng)詞,不要使用do或does
- (BOOL)canHide; //推薦
- (BOOL)shouldCloseDocument; //推薦
- (BOOL)doesAcceptGlyphInfo; //反對(duì)
二肠套、代碼注釋規(guī)范
優(yōu)秀的代碼大部分是可以自描述的舰涌,我們完全可以用代碼本身來表達(dá)它到底在干什么,而不需要注釋的輔助你稚。
但并不是說一定不能寫注釋瓷耙,有以下三種情況比較適合寫注釋:
公共接口(注釋要告訴閱讀代碼的人,當(dāng)前類能實(shí)現(xiàn)什么功能)刁赖。
涉及到比較深層專業(yè)知識(shí)的代碼(注釋要體現(xiàn)出實(shí)現(xiàn)原理和思想)搁痛。
容易產(chǎn)生歧義的代碼(但是嚴(yán)格來說,容易讓人產(chǎn)生歧義的代碼是不允許存在的)乾闰。
除了上述這三種情況落追,如果別人只能依靠注釋才能讀懂你的代碼的時(shí)候盈滴,就要反思代碼出現(xiàn)了什么問題涯肩。
最后,對(duì)于注釋的內(nèi)容巢钓,相對(duì)于“做了什么”病苗,更應(yīng)該說明“為什么這么做”。
1症汹、import注釋
如果有一個(gè)以上的import語句硫朦,就對(duì)這些語句進(jìn)行分組,每個(gè)分組的注釋是可選的背镇。
// Frameworks
#import ;
// Models
#import "NYTUser.h"
// Views
#import "NYTButton.h"
#import "NYTUserView.h"
2咬展、屬性注釋
寫在屬性之后泽裳,用兩個(gè)空格隔開
==例:==
@property (nonatomic, readwrite, strong) UIView *headerView; //注釋
3、方法聲明注釋:
一個(gè)函數(shù)(方法)必須有一個(gè)字符串文檔來解釋破婆,除非它:
非公開涮总,私有函數(shù)。
很短祷舀。
顯而易見瀑梗。
而其余的,包括公開接口裳扯,重要的方法抛丽,分類,以及協(xié)議饰豺,都應(yīng)該伴隨文檔(注釋):
以/開始
第二行是總結(jié)性的語句
第三行永遠(yuǎn)是空行
在與第二行開頭對(duì)齊的位置寫剩下的注釋亿鲜。
建議這樣寫:
/This comment serves to demonstrate the format of a doc string.
Note that the summary line is always at most one line long, and after the opening block comment,
and each line of text is preceded by a single space.
*/
方法的注釋使用Xcode自帶注釋快捷鍵:Commond+option+/
==例:==
/**
@param tableView
@param section
<a >@return</a>
*/
- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
//...
}
4、代碼塊注釋
單行的用//+空格開頭冤吨,多行的采用/* */注釋
5狡门、TODO
使用//TODO:說明 標(biāo)記一些未完成的或完成的不盡如人意的地方
==例:==
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//TODO:增加初始化
return YES;
}
三、代碼格式化規(guī)范
1锅很、指針*位置
定義一個(gè)對(duì)象時(shí)其馏,指針*靠近變量
==例:== NSString *userName;
2、方法的聲明和定義
在 - 爆安、+和 返回值之間留一個(gè)空格叛复,方法名和第一個(gè)參數(shù)之間不留空格
==例:==
- (void)insertSubview:(UIView *)view atIndex:(NSInteger)index;
3、代碼縮進(jìn)
不要在工程里使用 Tab 鍵扔仓,使用空格來進(jìn)行縮進(jìn)褐奥。在 Xcode > Preferences > Text Editing 將 Tab 和自動(dòng)縮進(jìn)都設(shè)置為 4 個(gè)空格
Method與Method之間空一行
一元運(yùn)算符與變量之間沒有空格、二元運(yùn)算符與變量之間必須有空格
==例:==
!bValue
fLength = fWidth * 2;
- (void)sampleMethod1;
- (void)sampleMethod2;
4翘簇、對(duì)method進(jìn)行分組
使用#pragma mark -對(duì)method進(jìn)行分組
#pragma mark - Life Cycle Methods
- (instancetype)init
- (void)dealloc
- (void)viewWillAppear:(BOOL)animated
- (void)viewDidAppear:(BOOL)animated
- (void)viewWillDisappear:(BOOL)animated
- (void)viewDidDisappear:(BOOL)animated
#pragma mark - Override Methods
#pragma mark - Intial Methods
#pragma mark - Network Methods
#pragma mark - Target Methods
#pragma mark - Public Methods
#pragma mark - Private Methods
#pragma mark - UITableViewDataSource
#pragma mark - UITableViewDelegate
#pragma mark - Lazy Loads
#pragma mark - NSCopying
#pragma mark - NSObject Methods
5撬码、大括號(hào)寫法
對(duì)于類的method:左括號(hào)另起一行寫(遵循蘋果官方文檔)
對(duì)于其他使用場(chǎng)景(if,for,while,switch等): 左括號(hào)跟在第一行后邊
==例:==
- (void)sampleMethod
{
BOOL someCondition = YES;
if(someCondition) {
// do something here
}
}
6、property變量
==例:==
@property (nonatomic, readwrite, strong) UIView *headerView; //注釋
四版保、編碼規(guī)范
1呜笑、if語句
①、須列出所有分支(窮舉所有的情況)彻犁,而且每個(gè)分支都須給出明確的結(jié)果叫胁。
==推薦這樣寫:==
var hintStr;
if (count < 3) {
hintStr = "Good";
} else {
hintStr = "";
}
==不推薦這樣寫:==
var hintStr;
if (count < 3) {
hintStr = "Good";
}
②、不要使用過多的分支汞幢,要善于使用return來提前返回錯(cuò)誤的情況驼鹅,把最正確的情況放到最后返回。
==推薦這樣寫:==
if (!user.UserName) return NO;
if (!user.Password) return NO;
if (!user.Email) return NO;
return YES;
==不推薦這樣寫:==
BOOL isValid = NO;
if (user.UserName)
{
if (user.Password)
{
if (user.Email) isValid = YES;
}
}
return isValid;
③、條件過多输钩,過長(zhǎng)的時(shí)候應(yīng)該換行豺型。條件表達(dá)式如果很長(zhǎng),則需要將他們提取出來賦給一個(gè)BOOL值买乃,或者抽取出一個(gè)方法
==推薦這樣寫:==
if (condition1 &&
condition2 &&
condition3 &&
condition4) {
// Do something
}
BOOL finalCondition = condition1 && condition2 && condition3 && condition4
if (finalCondition) {
// Do something
}
if ([self canDelete]){
// Do something
}
- (BOOL)canDelete
{
BOOL finalCondition1 = condition1 && condition2
BOOL finalCondition2 = condition3 && condition4
return condition1 && condition2;
}
==不推薦這樣寫:==
if (condition1 && condition2 && condition3 && condition4) {
// Do something
}
④触创、條件語句的判斷應(yīng)該是變量在右,常量在左为牍。
==推薦:==
if (6 == count) {
}
if (nil == object) {
}
if (!object) {
}
==不推薦:==
if (count == 6) {
}
if (object == nil) {
}
if (object == nil)容易誤寫成賦值語句,if (!object)寫法很簡(jiǎn)潔
⑤哼绑、每個(gè)分支的實(shí)現(xiàn)代碼都須被大括號(hào)包圍
==推薦:==
if (!error) {
return success;
}
==不推薦:==
if (!error)
return success;
可以如下這樣寫:
if (!error) return success;
2、for語句
①碉咆、不可在for循環(huán)內(nèi)修改循環(huán)變量抖韩,防止for循環(huán)失去控制。
for (int index = 0; index < 10; index++){
...
logicToChange(index)
}
②疫铜、避免使用continue和break茂浮。
continue和break所描述的是“什么時(shí)候不做什么”,所以為了讀懂二者所在的代碼壳咕,我們需要在頭腦里將他們?nèi)》础?
其實(shí)最好不要讓這兩個(gè)東西出現(xiàn)席揽,因?yàn)槲覀兊拇a只要體現(xiàn)出“什么時(shí)候做什么”就好了,而且通過適當(dāng)?shù)姆椒ㄎ嚼澹强梢詫⑦@兩個(gè)東西消滅掉的:
如果出現(xiàn)了continue幌羞,只需要把continue的條件取反即可
var filteredProducts = Array()
for level in products {
if level.hasPrefix("bad") {
continue
}
filteredProducts.append(level)
}
我們可以看到,通過判斷字符串里是否含有“bad”這個(gè)prefix來過濾掉一些值竟稳。其實(shí)我們是可以通過取反属桦,來避免使用continue的:
for level in products {
if !level.hasPrefix("bad") {
filteredProducts.append(level)
}
}
消除while里的break:將break的條件取反,并合并到主循環(huán)里
在while里的break其實(shí)就相當(dāng)于“不存在”他爸,既然是不存在的東西就完全可以在最開始的條件語句中將其排除聂宾。
while里的break:
while (condition1) {
...
if (condition2) {
break;
}
}
取反并合并到主條件:
while (condition1 && !condition2) {
...
}
在有返回值的方法里消除break:將break轉(zhuǎn)換為return立即返回
有人喜歡這樣做:在有返回值的方法里break之后,再返回某個(gè)值诊笤。其實(shí)完全可以在break的那一行直接返回系谐。
func hasBadProductIn(products: Array) -> Bool {
var result = false
for level in products {
if level.hasPrefix("bad") {
result = true
break
}
}
return result
}
遇到錯(cuò)誤條件直接返回:
func hasBadProductIn(products: Array) -> Bool {
for level in products {
if level.hasPrefix("bad") {
return true
}
}
return false
}
這樣寫的話不用特意聲明一個(gè)變量來特意保存需要返回的值,看起來非常簡(jiǎn)潔讨跟,可讀性高纪他。
3、Switch語句
①许赃、每個(gè)分支都必須用大括號(hào)括起來
推薦這樣寫:
switch (integer) {
case 1: {
// ...
}
break;
case 2: {
// ...
break;
}
default:{
// ...
break;
}
}
②止喷、使用枚舉類型時(shí)馆类,不能有default分支混聊, 除了使用枚舉類型以外,都必須有default分支
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;
switch (menuType) {
case RWTLeftMenuTopItemMain: {
// ...
break;
}
case RWTLeftMenuTopItemShows: {
// ...
break;
}
case RWTLeftMenuTopItemSchedule: {
// ...
break;
}
}
在Switch語句使用枚舉類型的時(shí)候,如果使用了default分支句喜,在將來就無法通過編譯器來檢查新增的枚舉類型了预愤。
4、函數(shù)
①咳胃、一個(gè)函數(shù)只做一件事(單一原則)
每個(gè)函數(shù)的職責(zé)都應(yīng)該劃分的很明確(就像類一樣)植康。
==推薦:==
dataConfiguration()
viewConfiguration()
==不推薦:==
void dataConfiguration()
{
...
viewConfiguration()
}
②、對(duì)于有返回值的函數(shù)(方法)展懈,每一個(gè)分支都必須有返回值
==推薦:==
int function()
{
if(condition1){
return count1
}else if(condition2){
return count2
}else{
return defaultCount
}
}
==不推薦:==
int function()
{
if(condition1){
return count1
}else if(condition2){
return count2
}
}
③销睁、對(duì)輸入?yún)?shù)的正確性和有效性進(jìn)行檢查,參數(shù)錯(cuò)誤立即返回
==推薦:==
void function(param1,param2)
{
if(param1 is unavailable){
return;
}
if(param2 is unavailable){
return;
}
//Do some right thing
}
④存崖、如果在不同的函數(shù)內(nèi)部有相同的功能冻记,應(yīng)該把相同的功能抽取出來單獨(dú)作為另一個(gè)函數(shù)
原來的調(diào)用:
void logic() {
a();
b();
if (logic1 condition) {
c();
} else {
d();
}
}
將a来惧,b函數(shù)抽取出來作為單獨(dú)的函數(shù)
void basicConfig() {
a();
b();
}
void logic1() {
basicConfig();
c();
}
void logic2() {
basicConfig();
d();
}
⑤冗栗、將函數(shù)內(nèi)部比較復(fù)雜的邏輯提取出來作為單獨(dú)的函數(shù)
一個(gè)函數(shù)內(nèi)的不清晰(邏輯判斷比較多,行數(shù)較多)的那片代碼供搀,往往可以被提取出去隅居,構(gòu)成一個(gè)新的函數(shù),然后在原來的地方調(diào)用它這樣你就可以使用有意義的函數(shù)名來代替注釋葛虐,增加程序的可讀性胎源。
舉一個(gè)發(fā)送郵件的例子:
openEmailSite();
login();
writeTitle(title);
writeContent(content);
writeReceiver(receiver);
addAttachment(attachment);
send();
中間的部分稍微長(zhǎng)一些,我們可以將它們提取出來:
void writeEmail(title, content,receiver,attachment)
{
writeTitle(title);
writeContent(content);
writeReceiver(receiver);
addAttachment(attachment);
}
然后再看一下原來的代碼:
openEmailSite();
login();
writeEmail(title, content,receiver,attachment)
send();
參考資料:
iOS 代碼規(guī)范
iOS開發(fā)總結(jié)之代碼規(guī)范
iOS開發(fā)代碼規(guī)范(通用)
Objective-C開發(fā)編碼規(guī)范
【iOS】命名規(guī)范
Ios Code Specification
Apple Coding Guidelines for Cocoa
Google Objective-C Style Guide