iOS開發-Objective-C語言代碼規範-OC代碼規範集合-iOS代碼規範

爲什麼要做代碼規範?

目前的項目大多都是由一個團隊來完成,如果沒有統一的代碼規範,那麼每個人的代碼必定會風格迥異,在工作中肯定會有多個人同時開發同一模塊的情況,即使是分工十分明晰的,等到整合代碼、CodeReView、工作接力等情況時問題就會顯現出來。統一的風格使得代碼可讀性大大提高了,人們看到任何一段代碼都不用去浪費更多的時間去琢磨。規範不是對開發的制約,而確實是有助於提高開發效率的。,規範的代碼在團隊的合作開發中是非常有益而且必要的。

代碼規範能帶來什麼好處?

1.規範的代碼可以促進團隊合作

在文章開篇的時候就已經提及了:統一的風格使得代碼可讀性提高,在同事之間看到任何一段代碼都不用去浪費更多的時間去琢磨,可以高效的完成開發工作。

2.規範的代碼可以減少bug處理

有規範的對參數進行輸入輸出,有規範的異常處理,沒規範的日誌處理等等,bug不但可以有效減少,查找bug也變得輕而易舉。

3.規範的代碼可以降低維護成本

開發過程中的代碼質量直接影響着維護的成本,可讀性高的代碼維護成本必然會大大降低。 而且,維護工作不僅僅是讀懂原有代碼,還需要在原有代碼基礎上作出修改,因此,統一的風格有利於長期的維護。

4.規範的代碼有助於代碼審查

代碼審查可以及時糾正一些錯誤,對開發人員的代碼規範作出監督。團隊的代碼審查同時也是一個很好的學習機會,對成員的進步也是很有益的,同時代碼審查也有助於代碼規範的實施。

5.養成代碼規範的習慣,有助於自身的成長

有很多時候去看自己曾經寫得代碼是不是沒有頭緒呢?尤其是出現bug的時候需要逐行的debug?我們應該做的就是規範開發,減少自己出現的錯誤,規範開發最大的受益人其實是自己。


下面開始我們的正題:代碼規範

《iOS技術部門代碼規範1.0範本》

本文整合了谷歌、raywenderlich.com、58到家、NetStars(日)公司在開發過程中關於iOS端(Objective-C語言)的代碼規範,適用於中小型團隊(0-30人協作)。

核心原則 意義
原則一:代碼應該簡潔易懂,邏輯清晰 軟件是需要人來維護的,不要過分追求技巧,降低程序的可讀性。
原則二:面向變化編程,而不是面向需求編程 不能僅僅爲了當前的需求,寫出擴展性強,易修改的程序纔是負責任的做法,對自己負責,對公司負責。
原則三:先保證程序的正確性,防止過度工程 過度工程(over-engineering):在正確可用的代碼寫出之前就過度地考慮擴展,重用的問題,使得工程過度複雜。

引用改編自 https://www.jianshu.com/p/d7e87107073c

目錄:

  • 註釋的寫法
  • 命名與規範
  • 分類的方法規範
  • 代碼組織結構規範
  • 條件語句規範
  • 補充規範

註釋的寫法

首先我們就從大家最關心的“代碼註釋”作爲切入,雖然寫起來很痛苦,但註釋是保證代碼可讀性的關鍵。下面的規則給出了你應該什麼時候、在哪進行註釋。註釋很重要,好的代碼應該能自成文檔。與其給類型及變量起一個晦澀難懂的名字,再爲它寫註釋,不如直接起一個有意義的名字。當你寫註釋的時候,記得你是在給你的聽衆寫,即下一個需要閱讀你所寫代碼的貢獻者。大方一點,下一個讀代碼的人可能就是你😁

1.Class 類註釋

每個接口、類別以及協議應輔以註釋,以描述它的目的及與整個項目的關係。如下:

/**
 委託代理,用來處理關於app的啓動和關閉過程中的xxx、xxx事件,被app的xxx控制器擁有。
*/ 
@interface MyAppDelegate : NSObject {
  ...
}
@end
2.Property 屬性註釋
/**
 標識是起始地還是目的地
 */
@property (nonatomic, copy) NSString *userName;

/**
 地址信息模型
 */
@property (nonatomic, copy) UserInfoModel *userInfoModel;
3.Method 方法聲明註釋
/**
 登錄倒計時文本
 @param timerLabel 倒計時Label對象
 @param timeText   倒計時文本數據
*/
- (void)timerLabel:(UILabel *)timerLabel timeText:(NSString *)timeText;
4.Block 代碼塊註釋
/**
 驗證搜索成功失敗模塊
 @param searchResponse 搜索返回結果
 @param error 錯誤信息
 @return 如果返回 YES 代表搜索成功, 返回 NO 代表搜索失敗
*/
typedef BOOL(^SearchResultBlock)(AMapPOISearchResponse *searchResponse, NSError *error);
5.NSEnum 枚舉註釋
/**
(參考YYText)
YYTextVerticalAlignmentTop: 頂部對齊
YYTextVerticalAlignmentCenter: 居中對齊
YYTextVerticalAlignmentBottom: 底部對齊
*/
typedef NS_ENUM(NSInteger, YYTextVerticalAlignment) {
    YYTextVerticalAlignmentTop =    0, ///< Top alignment.
    YYTextVerticalAlignmentCenter = 1, ///< Center alignment.
    YYTextVerticalAlignmentBottom = 2, ///< Bottom alignment.
};
6.局部變量註釋
@interface SomeViewController () {
    SomeModel * someModel; // 用來存儲信息
    NSString * someStr;    // 用來檢測的字段
    UIView * someView;     // 用來顯示信息的視圖
}
7.Method 方法實現註釋①
/** 地址選擇確認返回上一頁 */
- (void)doSomeThing:(id)someObject{
    /**
     判斷內容和結果
    */ 
    if (條件判斷1) {
        if (內部條件) {
            do...
        }else{
            do...
        }
    }
    
    /**
     判斷內容和結果
    */ 
    if (條件判斷2) {
        do...
        return;
    }
    
    /**
     [回調數據][目標接受頁面]
     tips:當block嵌套太多的時候,對於讀代碼的人來說非常困惑,因爲往往可能追了3,4個block進去之後,被以下這點代碼傳出去了,所以,在使用block的時候,花點時間註明回調的數據以及目標頁面方便自己也方便別人
    */ 
    _someBlock(xxx);
}
8.Method 方法實現註釋②
// tips:對於一個不常用的第三方、底層類庫等代碼,可以簡單的把每一個過程都寫一下,這樣的話易讀性很強
- (void)createCAEmitterLayer{
    // 1.創建CAEmitterLayer
    CAEmitterLayer *emitterLayer = [CAEmitterLayer layer];
    
    // 2.設置CAEmitterLayer的屬性(最主要的是前四個)
    // 發射源的形狀 是枚舉類型
    emitterLayer.emitterShape = kCAEmitterLayerLine;
    
    // 發射模式 枚舉類型
    emitterLayer.emitterMode = kCAEmitterLayerSurface;
    
    // 發射源的size 決定了發射源的大小,如果做了傾斜或者便宜屏幕寬度是不夠的,那時候就需要自定義
    emitterLayer.emitterSize = self.view.frame.size;
    
    // 發射源的位置
    emitterLayer.emitterPosition = CGPointMake(self.view.bounds.size.width * 0.5, -10);
    
    // 渲染模式 枚舉類型 (⭐️這個渲染模式表達效果會很不好,不常用,kCAEmitterLayerAdditive,可以讓重疊的部分高亮)
    emitterLayer.renderMode = kCAEmitterLayerAdditive;
    
    // 3.添加到目標視圖的layer上
    [self.view.layer addSublayer:emitterLayer];
}
9.Method 方法實現註釋③
/** 
 登錄請求驗證
 @param userId 用戶名
 @param password 密碼
 @param complete 執行完畢的block
*/
- (void)loginRequestWithUserId:(NSString *)userId password:(NSString *)password complete:(void (^)(CheckLogon *result))complete{
	
}

命名的規範

1.通用變量命名

保持使用駝峯命名法,建議的寫法如下:

@property (nonatomic, copy) NSArray *childPaths;

反例:

@property (nonatomic, copy) NSArray *childpaths;
2.宏命名

不帶參數情況下,全部大寫,單詞間用 _ 分隔,建議寫法如下:

#define THIS_IS_A_TICKET @"THIS_IS_A_TICKET"

不帶參數情況下,避免和類自己的參數衝突可以以【小寫】字母k或者自定義字母作爲開頭,後面遵循駝峯命名,建議寫法如下:

#define kScreenWidth [UIScreen mainScreen].bounds.size.width

帶參數的情況下,遵循駝峯命名,建議寫法如下:

#define getImageUrl(url) [NSURL URLWithString:[NSString stringWithFormat:@"%@%@",kBaseUrl,url]]

反例:

#define homepageadbannerheight 85
3.控件命名

控件的命名需要注意突出後綴,建議寫法如下:

UIView *userInfoView;

反例:

UIView *userInfo;

關鍵是讓人通過命名就能知道其目的或者用法,一般控件的命名都是接上所生命類的後綴,示例情況如下:

UI控件名 自定義名
UIView someView
UViewController someViewController
UIButton someButton
UILabel someLabel
UITableView someTableView
4.類的命名①

一般在自定義類的時候需要注意命名規範,首字母大寫每個單詞首字母大寫(大駝峯命名法),建議寫法如下:

@interface UserInfoManager : NSObject
5.類的命名②

一般在不同業務線上會有不同分支的首頁、子頁面,很多時候會有重複的信息,比如兩個模塊都有HomePage,這時候就需要在前面加上前綴,建議寫法如下:

/**
 活動頁面
*/
@interface ActivityHomeViewController : UIViewController

反例:

@interface HomeViewController : UIViewController

分類的方法規範

我們在開發過程中爲了更好的使用系統方法,一般會使用分類來添加方法,來滿足開發需要,分類的作用就是在不修改原有類的基礎上,爲一個類擴展方法,最主要的是可以給系統類擴展我們自己定義的方法。爲了避免和系統方法或者別的同事所做的分類方法的衝突,一般都會在方法面前添加小寫字母來做區別,建議寫法如下:

@interface UIView (TouchBlock)
- (void)tb_touchView:(void (^)(void))block;
@end

反例:

@interface UIView (TouchBlock)
- (void)touchView:(void (^)(void))block;
@end

代碼組織結構規範

1. #import 文件的順序

在開發過程中,使用#import來導入頭文件是必不可少的工序,但有的功能或者業務模塊需要導入的文件就會非常多,看着會很彆扭,所以指定一套方案來改善這一現狀是很有必要的,建議順序如下:

// 系統庫
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>

// 第三方庫
#import <Masonry.h>

// 自定義類
#import "MyButton.h" 
#import "UserInfoModel.h"
2.控制器中方法分區

一般在控制器中,會有控制器生命週期相關方法以及不同的業務代碼,可以使用#pragma mark來進行簡單的方法分區,建議寫法如圖:
在這裏插入圖片描述
可以稍做優化,區分爲幾個模塊,示例如下:

#pragma mark - Life Cycle 生命週期
#pragma mark - Request 網絡請求
#pragma mark - Delegate 代理實現
#pragma mark - Event Response 事件響應
#pragma mark - Methods 方法

根據每個人的喜好不一致可以按照自己的想法來寫,這樣在別人梳理文件的時候思路會比較清晰


條件語句規範

1.{}括號的規範

一般會出現以下幾種情況:

// 1
if (YES) {
    Do();
}

// 2
if (YES) DO();

// 3
if (YES)
   Do();
   
// 4
if (YES) 
{
   Do(); 
}

以上第一種方式是蘋果主推的,經過調研,方法2、3、4雖然在寫法上會有所簡化,但在風格上看來還是有所欠缺,所以更多的建議使用第一種。

2.減少邏輯嵌套

一般在判斷的時候可以使用最簡單的方法來做最簡單的事情,使用return可以減少複雜度,提高代碼可讀性,建議寫法如下:

- (void)someMethod {
    if(![someThing]) {
        return;
    }
    // continue do something 
}

反例:

- (void)someMethod {
    if([someThing]) {
        // continue do something
    }else{
    	return;
    }
}
3.複雜表達式

常用寫法:

if ([self getUserInfoModel] == nil && ![self userLogin]){
	[self showLoginVC];
}

一般會有很長的表達式或者多個待判斷的值,在這種情況下,建議以下寫法:

BOOL userInfoModelIsNil  = [self getUserInfoModel] == nil;
BOOL userIsLogin = ![self userLogin];
BOOL showLoginVC = userModelIsNil && userIsLogin;
if (showLoginVC) {
    [self showLoginVC];
}

第一種寫法大家都是很常用的,表達很簡潔,但是從閱讀代碼和調試代碼的角度看,推薦第二種,因爲每個條件和句子的意義很明顯的就能看出來。


補充規範

1.如果一行有非常多的參數,更好的方式是將每個參數單獨拆成一行。如果使用多行,將每個參數前的冒號對齊,建議寫法如下:

- (void)doSomethingWithUserName:(NSString *)name
                        address:(NSString *)address
                     doorNumber:(float)number {
  ...
}

當第一個關鍵字比其它的短時,保證下一行至少有 4 個空格的縮進。這樣可以使關鍵字垂直對齊,而不是使用冒號對齊:

- (void)getA:(AClass *)A
    longKeywordB:(BClass *)B
    evenLongerKeywordC:(CClass *)C {
  ...
}

2.常量是容易重複被使用和無需通過查找和代替就能快速修改值。常量應該使用static來聲明而不是使用#define,除非顯式地使用宏。建議寫法如下:

static NSString * const XXXAboutViewControllerCompanyName = @"XXXCompanyName";
static CGFloat const XXXImageThumbnailHeight = 50.0;

反例:

#define CompanyName @"XXXCompanyName"
#define ImagethumbnailHeight 50

3.布爾值的判斷書寫,Objective-C使用YES和NO。因爲true和false應該只在CoreFoundation,C或C++代碼使用。既然nil解析成NO,所以沒有必要在條件語句比較。不要拿某樣東西直接與YES比較,因爲YES被定義爲1和一個BOOL能被設置爲8位。這是爲了在不同文件保持一致性和在視覺上更加簡潔而考慮。建議書寫方式如下:

if (someObject) {

}
if (![anotherObject boolValue]) {

}

反例:

if (someObject == nil) {

}
if ([anotherObject boolValue] == NO) {

}
if (isAwesome == YES) {

} 
if (isAwesome == true) {

}

4.三元操作符的表達,當需要提高代碼的清晰性和簡潔性時,三元操作符?:纔會使用。單個條件求值常常需要它。多個條件求值時,如果使用if語句或重構成實例變量時,代碼會更加易讀。一般來說,最好使用三元操作符是在根據條件來賦值的情況下。Non-boolean的變量與某東西比較,加上括號()會提高可讀性。如果被比較的變量是boolean類型,那麼就不需要括號。建議寫法如下:

NSInteger value = 5;
result = (value != 0) ? x : y;

BOOL isHorizontal = YES;
result = isHorizontal ? x : y;

反例:

result = a > b ? x = c > d ? c : d : y;

5.在使用 Init方法 和 類構造方法 時需要注意:
Init方法應該遵循Apple生成代碼模板的命名規則。返回類型應該使用instancetype而不是id

- (instancetype)init {
  self = [super init];
  if (self) {
    // ...
  }
  return self;
}

當類構造方法被使用時,它應該返回類型是instancetype 而不是id。這樣確保編譯器正確地推斷結果類型。

@interface Airplane
+ (instancetype)airplaneWithType:(RWTAirplaneType)type;
@end
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章