iOS開發(fā)規(guī)范

iOS開發(fā)規(guī)范

前言

由于每個(gè)開發(fā)者的編碼習(xí)慣和風(fēng)格都不一樣,為了保證開發(fā)效率,減輕代碼閱讀成本,隨著以后 APP 業(yè)務(wù)線越來越大,代碼量越來越多時(shí),能保障代碼的可維護(hù)性,降低維護(hù)成本,方便高效定位解決問題,所以從項(xiàng)目的細(xì)節(jié)到整體都希望根據(jù)此文檔來達(dá)到風(fēng)格統(tǒng)一,利于維護(hù)。

一、命名規(guī)范

不論是類名,屬性,變量,常量,方法,命名應(yīng)該清晰明了,表達(dá)準(zhǔn)確,能夠望名知意,盡量不要使用縮寫,更不要使用中文拼音。

1.1 類(Class)

  • 類名統(tǒng)一使用 BC 開頭作為前綴,每個(gè)單詞首字母大寫(大駝峰命名)。

示例:

///(C)控制器
BCExchangeRecordController
///(V)視圖
BCExchangeRecordCell
///(M)模型
BCExchangeRecordModel

1.2 縮寫

通常,我們都不應(yīng)該縮寫命名。然而,下面所列舉的都是一些眾所周知的縮寫,我們可以繼續(xù)使用這些古老的縮寫。在其他情況下,我們需要遵循下面兩條縮寫建議:

  • 允許使用那些在C語言時(shí)代就已經(jīng)在使用的縮寫,比如alloc和getc。
  • 我們可以在命名參數(shù)的時(shí)候使用縮寫。其他情況,盡量不要使用縮寫。

我們也可以使用計(jì)算機(jī)行業(yè)通用的縮寫。包括但不限于HTML、URL、RTF、HTTP、TIFF、JPG、PNG、GIF、LZW、ROM、RGB、CMYK、MIDI、FTP。

1.3 屬性&局部變量&成員變量&參數(shù)名

  • ==盡量不要使用成員變量,改為使用屬性(property)。==

  • 屬性或變量以小寫字母開頭,后面每個(gè)單詞首字母大寫(小駝峰命名)。

示例:

/// 版本號
@property (nonatomic, copy) NSString *versionNumber;

1.4 宏定義&局部常量&全局常量

  • 局部常量使用小寫字母 k 開頭,后面單詞首字母大寫(小駝峰命名)。
  • 常量宏每個(gè)字母都采用大寫,單詞之間可以使用下劃線分割。
  • 預(yù)編譯宏每個(gè)字母都采用大寫,單詞之間可以使用下劃線分割。
  • 全局常量使用前綴 BC 開頭,后面單詞首字母大寫(小駝峰命名)。定義在公共的 .h 文件中,在 .m 文件中寫實(shí)現(xiàn)。

示例:

/// 局部常量,只定義在 .m 文件中,不對外暴露
static const NSTimeInterval kAnimationDuration = 0.25;
static NSString *const kPropertyKey = @"xxxxx";

/// 常量宏
#define SCREEN_WIDTH ([[UIScreen mainScreen] bounds].size.width)
#define SCREEN_HEIGHT ([[UIScreen mainScreen] bounds].size.height)

/// 預(yù)編譯宏
#if RELEASE

#elif DEV

#endif

/**全局常量 */ 
/// 在 .h 文件定義
extern NSString *const BCUpdateUserInfoNotification;

/// 在 .m 文件實(shí)現(xiàn)
NSString *const BCUpdateUserInfoNotification = @"BCUpdateUserInfoNotification";

1.5 通知(Notification)

  • 通知(廣播)名使用大寫字母 BC 開頭,后面每個(gè)單詞首字母大寫(小駝峰命名),以 Notification 為后綴。定義在公共的 .h 文件中,在 .m 文件中寫實(shí)現(xiàn)。

示例:

/// 在 .h 文件定義
extern NSString *const BCUpdateUserInfoNotification;

/// 在 .m 文件實(shí)現(xiàn)
NSString *const BCUpdateUserInfoNotification = @"BCUpdateUserInfoNotification";

1.6 枚舉(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,
};

1.7 方法(Method)

  • 方法名采用小寫字母開頭的(小駝峰命名)方式。
  • 不要在方法名稱中使用前綴,不要使用 “_” 下劃線開頭。
  • 盡量避免方法參數(shù)過多導(dǎo)致方法太長,當(dāng)參數(shù)過多時(shí)(比如超過5個(gè)),可以封裝成模型(Model)對象 或者 使用字典(NSDictionary)傳入。

示例:

/** 類方法,無返回值 */
+ (void)appUpdateWithModel:(BCAppUpdateModel *)model;

/** 實(shí)例方法,有返回值 */
- (NSObject *)objectWithDictionary:(NSDictionary *)dict;

1.8 類別(Category)

避免category中的方法覆蓋系統(tǒng)方法和屬性。可以在自定義方法或者屬性前加前綴 bc 加下劃線來區(qū)分。
示例:

/// 屬性
@property (nonatomic, strong) NSURL *bc_imageURL;
/// 方法
- (void)bc_setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder;

二、代碼規(guī)范

2.1 屬性(property)

  • 屬性按照不同數(shù)據(jù)類型和作用,正確使用修飾符。

  • 屬性的關(guān)鍵字按照 原子性,讀寫,內(nèi)存管理的順序排列。

  • 屬性內(nèi)存管理修飾符:

    • NSString,block 使用 ==copy== 修飾。

    • OC 對象使用 ==strong==,==weak== 修飾。

    • Delegate 使用 ==weak== 修飾,防止循環(huán)引用。

    • 不可變的 NSArrty,NSDictionary,NSAttributedString 可使用 ==copy== 修飾。

    • 可變的 NSMutableArray,NSMutableDictionary,NSMutableAttributedString 必須使用 ==strong== 修飾,防止對其進(jìn)行可變操作時(shí)崩潰。

    • 使用 typedef 重定義的 OC 數(shù)據(jù)類型(如:NSTimeInterval,NSInteger),枚舉類型,普通的基本數(shù)據(jù)類型使用 ==assign== 修飾。

  • 接口屬性定義原則:

    • .h 文件中,只可暴露對外公開且需要被使用的屬性,對內(nèi)使用的寫在 .m 文件的類擴(kuò)展中,務(wù)必遵循面向?qū)ο蟮?封裝 特性。

    • 對于對內(nèi)需要讀寫操作,對外又能使用的屬性,只需要在 .h 文件中加上 readonly 修飾符即可,在 .m 文件的類擴(kuò)展中正常使用 property

    • 集合類:數(shù)組(NSArray),字典(NSDictionary)盡量使用泛型來指定對象類型,尤其是 模型數(shù)組。

    • 對于布爾(BOOL)類型,請盡量提供 getter=isXXX 方法。

  • ==所有的屬性都使用getter和setter==
    避免初始化屬性的位置比較隨意,有單獨(dú)添加一個(gè)初始化方法類似 setupView 的,有在 init 初始化的,各種情況都有,導(dǎo)致團(tuán)隊(duì)協(xié)作的時(shí)候代碼顯得非常亂。

示例:

/// .h 文件定義
@interface XJWebViewController : UIViewController

/// 是否隱藏 HUD,默認(rèn)顯示,如果隱藏則顯示進(jìn)度條,顯示 HUD 時(shí)當(dāng)前 view 不可交互
@property (nonatomic, assign, getter=isHideProgressLoadingView) BOOL hideProgressLoadingView;

/// JS 方法名數(shù)組, 用于 js 調(diào)用 native
@property (nonatomic, strong, nullable) NSMutableArray <NSString *> *jsMethodArray;

/// wkWebview,對外只讀,防止外界對它進(jìn)行初始化操作
@property (nonatomic, strong, readonly) WKWebView *wkWebView;

/// urlStr,提供給外界賦值
@property (nonatomic, copy) NSString *urlStr;

@end

/// .m 文件的類擴(kuò)展
@interface XJWebViewController ()

/// 對內(nèi)可讀寫操作的 wkWebView
@property (nonatomic, strong) WKWebView *wkWebView;

#pragma mark - getters and setters
 - (void)setWkWebView
 - (WKWebView *)wkWebView
 ....

@end

2.2 委托(protocol)

  • @protocol 的定義,不需要另外新建文件,統(tǒng)一寫在當(dāng)前類 .h 文件的頂部,并將當(dāng)前類使用 @class xxx; 的方式引入,寫在 @protocol 的定義上方。

  • @protocol 命名規(guī)則:使用類名+Delegate后綴,如:UITableViewDelegate。

  • 使用 @optional 修飾可以不實(shí)現(xiàn)的方法,使用 @required 修飾必須實(shí)現(xiàn)的方法。

  • 類的實(shí)例必須作為回調(diào)方法的參數(shù)之一。

    • 回調(diào)方法的參數(shù)只有類自己的情況,方法名要符合實(shí)際含義。

    • 回調(diào)方法存在兩個(gè)以上參數(shù)的情況,以類的名字開頭,以表明此方法是屬于哪個(gè)類的。

示例:

@class UITableView;

@protocol UITableViewDelegate <NSObject>

@required 
// do something...

@optional
/// 可通過實(shí)現(xiàn)此委托方法來設(shè)置 cell 的行高。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath;

@end

@interface UITableView : UIScrollView
/// 委托屬性,務(wù)必使用 weak 修飾
@property (nonatomic, weak) id<UITableViewDelegate> delegate;

@end

2.3 #import 使用

  • 當(dāng)一個(gè)類需要 import 其它類時(shí),盡量在 .m 文件中 ==#import "xxx.h"==,如果需要在 .h 中引用,可在 .h 文件的頂部使用 @class xxx; 引入,告訴編譯器有這個(gè)文件。這樣做也更遵循面向?qū)ο蟮?封裝 特性。

2.4 代碼注釋

  • 注釋統(tǒng)一寫在 屬性(字段),方法的上方。

  • 模型類的屬性(字段),必須寫注釋,表明該字段的含義及作用。

  • 委托協(xié)議方法,閉包(block)回調(diào)方法,及每個(gè)類(視圖類,控制器類,工具類,分類)的 .h 文件中暴露出來的屬性,函數(shù)接口,都必須寫注釋,表名屬性的作用,方法的用途和使用,及方法各參數(shù)的說明。

  • ==屬性聲明單行注釋==,以下兩種注釋方式可選,注釋符和文案之間需要使用空格分開,使用以下兩種注釋的好處是按住 command 鍵查看屬性時(shí)有注釋提示。

示例:

/// 注釋說明
@property (nonatomic, copy) NSString *placeholder; 

/** 注釋說明 */
@property (nonatomic, strong) UIColor *textColor; 
  • ==方法聲明多行注釋==,使用快捷鍵 option + command + / 即可自動(dòng)多行注釋。

示例:

/**
 調(diào)起第三方支付客戶端

 @param info 訂單信息,微信(傳PayReq對象);支付寶(傳加密后的訂單字符串)。
 @param callback 回調(diào),返回狀態(tài)碼及狀態(tài)信息。
 */
+ (void)payWithInfo:(id)info callback:(ResultCallback)callback;
  • ==其它代碼內(nèi)注釋==,使用 // 注釋 即可,UI,布局等可少些注釋,涉及到邏輯和業(yè)務(wù)的請務(wù)必多寫注釋,方便其他人閱讀代碼時(shí)能快速理解相關(guān)的邏輯業(yè)務(wù)。

  • 對于業(yè)務(wù)代碼注釋的內(nèi)容,相對于 ==“做了什么”==,更應(yīng)該說明 ==“為什么這么做”==。

三、格式規(guī)范

3.1 指針 * 位置

  • 定義對象屬性,變量時(shí),指針 * 的位置應(yīng)該偏向?qū)傩裕ㄗ兞浚┟@邊。示例:UITextField<UITextInput> *textInput;。

3.2 空格規(guī)范

  • 屬性的括號,修飾詞,屬性名之間需要使用空格。

  • 在類方法的 + 號 或者實(shí)例方法的 - 號 與返回值之間需要使用空格。

  • 方法名開始位置和返回值括號之間,及結(jié)尾處與;之間不需要使用空格。

  • 在實(shí)現(xiàn)方法時(shí),方法的結(jié)尾處與大括號{之間需要使用空格。

  • 代碼塊內(nèi)的條件分支,循環(huán),運(yùn)算等 (){} 之間需要使用空格。

  • 變量或者其它的操作數(shù)之間,進(jìn)行賦值,運(yùn)算時(shí)都需要使用空格。

3.3 代碼塊&縮進(jìn)

  • 統(tǒng)一使用 Xcode 的自動(dòng)縮進(jìn),快捷鍵 command + A 全選,control + I 縮進(jìn)。

  • 每個(gè)方法的代碼行數(shù)盡量控制在100行以內(nèi),超過100行時(shí)可將部分業(yè)務(wù)或邏輯代碼抽取成另一個(gè)方法來調(diào)用。

  • 避免代碼冗余,將多處同樣的代碼封裝成可復(fù)用的公共方法。

  • 代碼塊內(nèi)盡量減少不必要的空行,保持代碼塊的緊湊性。

  • 方法與方法之間至少需要保留一行空行。

3.4 大括號寫法

  • 方法中{}的使用,包括但不限于 if,for,while,switch 等所有場景,左花括號必須緊跟在第一行代碼后面,不要另起一行。

示例:

- (void)loadServersData {
    dispatch_async(dispatch_get_main_queue(), ^{
        for (NSObject *obj in self.dataSource) {
            // do something...  
        }
    });
}

3.5 if else 分支

  • 對于空 nil 條件判斷,應(yīng)使用 ! 運(yùn)算符,不應(yīng)使用 == 運(yùn)算符。

示例:

if (!_tableView) {
    // do something...
}
  • 須列出所有分支(窮舉所有的情況),而且每個(gè)分支都須給出明確的結(jié)果。

示例:

if (/* 條件表達(dá)式 */) {
    // do something...
} else {
    // do something...
}
  • 不要使用過多的分支,要善于使用 return 來提前返回錯(cuò)誤的情況,把最正確的情況放到最后返回。

示例:

if (!user.UserName || !user.UserName.length) return NO;
if (!user.Password || !user.Password.length) return NO;

// do something...

return YES;
  • 條件過多,過長的時(shí)候應(yīng)該換行。條件表達(dá)式如果很長,則需要將他們提取出來賦給一個(gè)BOOL值,或者抽取出一個(gè)方法。

示例:

if (condition1 && 
    condition2 && 
    condition3 && 
    condition4) {
    // do something...
}

if ([self canDelete]) {
    // do something...
}

- (BOOL)canDelete {
    BOOL finalCondition1 = condition1 && condition2;
    BOOL finalCondition2 = condition3 && condition4;
    return condition1 && condition2;
}

3.6 Switch 語句

  • 每個(gè)分支都必須用大括號括起來。

示例:

switch (integer) {  
  case 1:  {
    // do something... 
   }
    break;  
  case 2: {  
    // do something...
    break;  
  }  
  default:{
    // do something...  
    break; 
  }
}
  • 使用枚舉類型時(shí),不能有 default 分支, 除了使用枚舉類型以外,都必須有 default 分支。

示例:

 switch (self.networkState) {
   case EnumNetworkStateNormal: {
        // do something...
   }
        break;
   case EnumNetworkStateNetError: {
        // do something...
   }
        break;
   case EnumNetworkStateServerError: {
        // do something...
   }
        break;
}

3.7 Method 分組

  • 使用 #pragma mark - 對 Method 進(jìn)行分組。
@property (nonatomic, strong) UIButton *confirmButton;

#pragma mark - Life cycle
viewDidLoad
viewWillAppear
...

#pragma mark - UITableViewDataSource 
    // do something...
    
#pragma mark - UITableViewDelegate  
    // do something...
    
#pragma mark - CustomDelegate
    // do something...
    
#pragma mark - Event respopense
    // do something...
    
#pragma mark - Public Methods
    // do something...
    
#pragma mark - Private Methods
    // do something...

#pragma mark - Getters and setters
 - (void)setConfirmButton
 - (UIButton *)confirmButton
 ....
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容