基本原則:
清晰又簡潔的代碼當(dāng)然是最好,但簡潔不如清晰重要。不要使用單詞的簡寫,除了非常常用的簡寫以外,盡量使用單詞全稱。API的名稱不要有歧義,一看API就知道是以什么方式做了什么事情。
1、命名
類的命名:
大駝峰式命名:即每個單詞的首字母采用大些字母。
使用能夠反映類功能的名詞短語。
DoneCourseListView
分類(類別)命名:
與類命名相同,此外需添加要擴(kuò)展的類名和“+”
NSString+MTValidate
方法命名:
小駝峰式命名:首字母小寫,之后每個單詞首字母都大寫
方法名使用動詞短語。
- (void)setPostValue:(int)value
方法參數(shù)命名:
首字母小寫,之后每個單詞首字母都大寫
具有足夠的說明性,不需要添加類型前綴
- (void)sendUserInfo:(NSDictionary*)userInfo
宏命名:
全部大寫,單詞間用 _ 分隔。[不帶參數(shù)]
#define THIS_IS_AN_MACRO @"THIS_IS_AN_MACRO"
以字母 k 開頭,后面遵循大駝峰命名。[不帶參數(shù)]
#define kWidth self.frame.size.width
小駝峰命名。[帶參數(shù)]
#define getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]
枚舉類型命名:
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<<0,
UIControlStateDisabled=1<<1
};
分組命名:
使用英文,首字母大寫,之后每個單詞首字母都大寫
每個分組使用模塊的名字
使用的開源庫統(tǒng)一放在“Library”分組下
使用的公共組件統(tǒng)一放在“Common”分組下
視圖控制器及AppDelegate統(tǒng)一放在“Controllers”分組下
后綴要求:
視圖控制器的子類應(yīng)該以“ViewController”或者“Controller”做后綴
CourseViewController
試圖的子類應(yīng)該以“View”做后綴
CourseView
協(xié)議(委托)使用Delegate或者DataSource作為后綴
VideoPlayerDelegate
按鈕的子類應(yīng)添加后綴“Button”,UI控件以此類推
LoginButton
2、注釋
優(yōu)秀的代碼大部分是可以自描述的,我們完全可以用代碼本身來表達(dá)它到底在干什么,而不需要注釋的輔助,如果做不到命名盡量的見名知意的話,就可以適當(dāng)?shù)奶砑右恍┳⑨尰蛘適ark。
但是以下三種情況比較適合寫注釋:
1、公共接口(注釋要告訴閱讀代碼的人,當(dāng)前類能實(shí)現(xiàn)什么功能)。
2、涉及到比較深層專業(yè)知識的代碼(注釋要體現(xiàn)出實(shí)現(xiàn)原理和思想)。
3、容易產(chǎn)生歧義的代碼(但是嚴(yán)格來說,容易讓人產(chǎn)生歧義的代碼是不允許存在的)。
除了上述這三種情況,如果別人只能依靠注釋才能讀懂你的代碼的時候,就要反思代碼出現(xiàn)了什么問題。對于注釋的內(nèi)容,相對于“做了什么”,更應(yīng)該說明“為什么這么做”。
注釋示例:
1、屬性注釋
/// 學(xué)生
@property (nonatomic, strong) Student *student;
2、類注釋
/** 類信息。此注釋用在類聲明的開頭。
@TestClass
@這是一個測試類
*/
@interfaceTestClass :UIView
@end
3、方法命名注釋
/**
根據(jù)請求的 URL 與 parameters 同步取出緩存數(shù)據(jù)
@param URL 請求的URL
@param parameters 請求的參數(shù)
@return 緩存的服務(wù)器數(shù)據(jù)
*/
+ (id)httpCacheForURL:(NSString *)URL parameters:(id)parameters;
4、import注釋
如果有一個以上的import語句,就對這些語句進(jìn)行分組,每個分組的注釋是可選的。
// Frameworks
#import<Foundation/Foundation.h>;
// Models
#import "UserInfoModel.h"
// Views
#import "CourseCountView"
#import "MineHeaderView.h"
5、代碼塊注釋
單行的用//+空格開頭,多行的采用/* */注釋
// [self.allCourseV dataWithTitle:@"購買課時" count:[NSString stringWithFormat:@"%lu", (unsigned long)allCount] desc:@""];
6、TODO
使用//TODO:說明 標(biāo)記一些未完成的或完成的不盡如人意的地方
- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions
{
//TODO:增加初始化
return YES;
}
3、格式化代碼
1、 指針 "*" 位置
定義一個對象時,指針 "*" 靠近變量
NSString *userName;
2、 方法的聲明和定義
在 - 、+ 和 返回值 之間留一個空格,方法名和第一個參數(shù)之間不留空格
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil;
3、 代碼縮進(jìn)
- 使用 xcode 默認(rèn)縮進(jìn),即 tab = 4空格
- 使用 xcode 中 re-indent 功能定期對代碼格式進(jìn)行整理,步聚:點(diǎn)選要進(jìn)行重構(gòu)的文檔, Control+A 全選該文檔,然后選擇XCODE -> Editor -> Structure -> Re-Indent 即可對代碼進(jìn)行重構(gòu) !
- 相同類型變量聲明需要獨(dú)行聲明
4、對方法進(jìn)行分組
使用 #pragma mark -方式對類的方法進(jìn)行分組
方法與方法之間空一行
#pragma mark - private methods
- (**void**)samplePrivateMethod
{...}
- (**void**)sampleForIf
{...}
5、 大括號寫法
對于類的method: 左括號另起一行寫(遵循蘋果官方文檔)
對于其他使用場景(if,for,while,switch等): 左括號跟在第一行后邊
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
任何需要寫大括號的部分,不得省略
//錯誤示例
- (void)wrongExamples
{
BOOL someCondition = YES;
if (someCondition)
NSLog(@"this is wrong!!!");
while (someCondition)
NSLog(@"this is wrong!!!");
}
4、編碼規(guī)范
1、if語句
①、須列出所有分支(窮舉所有的情況),而且每個分支都須給出明確的結(jié)果。
推薦
var hintStr;
if (count <3) {
hintStr ="Good";
} else {
hintStr ="";
}
不推薦
var hintStr;
if (count <3) {
hintStr ="Good";
}
②、不要使用過多的分支,要善于使用return來提前返回錯誤的情況,把最正確的情況放到最后返回。
推薦
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;
③、條件過多,過長的時候應(yīng)該換行(1)。條件表達(dá)式如果很長,則需要將他們提取出來賦給一個BOOL值(2),或者抽取出一個方法(3)
(1)
if(condition1 &&
condition2 &&
condition3 &&
condition4) {
// Do something
}
(2)
BOOL finalCondition = condition1 && condition2 && condition3 && condition4
if (finalCondition) {
// Do something
}
(3)
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 (object == nil)容易誤寫成賦值語句, if (!object)寫法很簡潔
推薦
if (6== count) {
}
if (nil == object) {
}
if (!object) {
}
不推薦
if(count == 6) {
}
if(object == nil) {
}
⑤、每個分支的實(shí)現(xiàn)代碼都須被大括號包圍
推薦:
if (!error) {
return success;
}
也可以:
if (!error) return success;
不推薦
if (!error)
return success;
2、for語句
①、不可在for循環(huán)內(nèi)修改循環(huán)變量,防止for循環(huán)失去控制。
②、避免使用continue和break。
- continue和break所描述的是“什么時候不做什么”,所以為了讀懂二者所在的代碼,我們需要在頭腦里將他們?nèi)》础?/li>
- 最好不要讓這兩個東西出現(xiàn),因?yàn)槲覀兊拇a只要體現(xiàn)出“什么時候做什么”就好了,而且通過適當(dāng)?shù)姆椒?,是可以將這兩個東西消滅掉的:
- 如果出現(xiàn)了continue,只需要把continue的條件取反即可
var filteredProducts = Array()
for level in products {
if level.hasPrefix("bad") {
continue;
}
filteredProducts.append(level)
}
我們可以看到,通過判斷字符串里是否含有“bad”這個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之后,再返回某個值。其實(shí)完全可以在break的那一行直接返回。
func hasBadProductIn(products: Array<String>) -> Bool {
var result = false
for level in products {
if level.hasPrefix("bad") {
result = true
break
}
}
return result
}
//這樣寫的話不用特意聲明一個變量來特意保存需要返回的值,看起來非常簡潔,可讀性高。
func hasBadProductIn(products: Array<String>) -> Bool {
for level in products {
if level.hasPrefix("bad") {
return true
}
}
return false
}
3、Switch語句
①、每個分支都必須用大括號括起來
switch (integer) {
case 1: {
// ...
break;
}
case 2: {
// ...
break;
}
default: {
// ...
break;
}
}
②、使用枚舉類型時,不能有default分支, 除了使用枚舉類型以外,都必須有default分支,在Switch語句使用枚舉類型的時候,如果使用了default分支,在將來就無法通過編譯器來檢查新增的枚舉類型了。
RWTLeftMenuTopItemType menuType = RWTLeftMenuTopItemMain;
switch (menuType) {
case RWTLeftMenuTopItemMain: {
// ...
break;
}
case RWTLeftMenuTopItemShows: {
// ...
break;
}
case RWTLeftMenuTopItemSchedule: {
// ...
break;
}
}
4、函數(shù)
①、一個函數(shù)只做一件事(單一原則)
②、對于有返回值的函數(shù)(方法),每一個分支都必須有返回值
③、對輸入?yún)?shù)的正確性和有效性進(jìn)行檢查,參數(shù)錯誤立即返回
④、如果在不同的函數(shù)內(nèi)部有相同的功能,應(yīng)該把相同的功能抽取出來單獨(dú)作為另一個函數(shù)
⑤、將函數(shù)內(nèi)部比較復(fù)雜的邏輯提取出來作為單獨(dú)的函數(shù)
5、團(tuán)隊(duì)規(guī)范
說明:一個好的團(tuán)隊(duì),理所當(dāng)然有其嚴(yán)格的代碼規(guī)范,好的代碼不僅可以提高團(tuán)隊(duì)的開放效率,也更利于團(tuán)隊(duì)項(xiàng)目的后期維護(hù),統(tǒng)一的代碼風(fēng)格,也是團(tuán)隊(duì)的核心,所以規(guī)范代碼很有必要!
1、刪除多余的空行 所有方法與方法之間空1行 所有代碼塊之間空1行
2、刪除多余的注釋
刪除注釋掉的代碼
刪除沒有意義的注釋
3、刪除多余的方法
如果方法沒有使用到,請刪除它
如果方法沒有執(zhí)行任何業(yè)務(wù)邏輯,請刪除它或者給出一定注釋
4、刪除未被使用的資源文件
5、添加必要的注釋
所有.h 文件中的property 需要給出注釋
所有自定義的方法需要給出注釋
比較大的代碼塊需要給出注釋
所有代碼中出現(xiàn)的阿拉伯?dāng)?shù)字需要給出注釋
程序中出現(xiàn)加密/解密 邏輯的操作地方,需要給出注釋說明過程(無論是系統(tǒng)還是自定義)
6、整體代碼風(fēng)格需要統(tǒng)一
代碼后面的”{“ 不需要單獨(dú)占用一行
邏輯運(yùn)算符 與“|” 代碼之前空一格
“#pragmamark -” 與下面的代碼之前不要空行
遵循一般性的代碼規(guī)范
參考: