iOS-代碼規範

一   命名規則編程

 

比較經常使用的變量命名法則有三種:駝峯命名法;下劃線命名法;帕斯卡命名法數組

 

  • 駝峯命名法:用的最廣的命名法,變量名經常使用此命名,命名由一個單詞或多個單詞組合而成,首字母小寫其他單詞首字母大寫,如:「userName」;
    下劃線命名法:每一個單詞間使用下劃線「_」分割,全部字母均小寫,如:「user_name」;
    安全

  • 帕斯卡命名法:每一個單詞的首字母均大寫的一串字符,與「駱駝命名法」的區別在於前者的首字母大寫,後者的首字母小寫。如:「UserName」。app

  •  

 

1.項目命名規則框架

Xcode項目的命名我的推薦使用帕斯卡命名法則。IXueDaiide

 

2..類命名規則函數

帕斯卡命名法則。XHLoginViewController性能

咱們在構建應用程序時,頗有可能會有部分代碼用於後續的項目,或者發佈出去供他人使用,那麼在別人使用你的類庫或者你引用其餘人的類庫時頗有可能出現相同命名的狀況,從而引起出「重複符號錯誤」(duplicate symbol error),爲了不這種使人惱怒的狀況發生,咱們應該習慣於給本身的類添加一個前綴,能夠是公司名稱的縮寫,也能夠是你我的姓名的縮寫,還有多是框架名稱的縮寫,Xcode設置類前綴的地方在這裏:測試

 

這裏須要注意的是,推薦你們使用三個字母以上做爲類的前綴,不少開發者都習慣於使用兩個字母做爲前綴(做者本人之前也是),可是蘋果公司保留了兩個字母做爲類前綴的權利,你們能夠發現蘋果公司的類都是以框架名縮寫而且都是兩個字母做爲前綴的,爲了不蘋果公司未來新發布的框架縮寫和你的前綴發生衝突,因此採用三個字母以上的前綴命名方案。不光類名如此,類目及延展或者自定義的結構體等一樣推薦使用前綴。動畫

 

項目中添加plist類型文件,不要命名爲info.plist,以防止和系統自帶的文件重名,發生莫名其妙的問題;

 

3.變量命名規則

  • 普通變量(修飾+類型)
    1
    2

    @property (nonatomic, strong) UILabel *titleLabel; //表示*標題*的label,是*UILabel*類型
    @property (nonatomic, strong) UIButton *confirmButton; //表示*確認*的button,是*UIButton*類型

     

  • 若是是聲明BOOL類型,建議在括號中重寫get方法 

    1

    @property (nonatomic, readonly, getter = isKeyWindow) BOOL keyWindow;

     

     

  • NS_ENUM 和  NS_OPTIONS 宏來定義枚舉類型

     
  • 在常量前邊加上字母k做爲標記
    static const NSTimeInterval kAnimationDuration = 0.3
  • 一些公開的常量一般使用類名做爲前綴,一樣是避免命名衝突而引起問題。案例: 
  • UIKIT_EXTERN NSString *const UIApplicationDidEnterBackgroundNotification// 常量命名

  • 通知命名 使用const修飾,以Notification結尾
    通知命名規則: [觸發通知的類名] + [Did | Will] + [動做] + Notification
    錯誤示例:
    UIKIT_EXTERN NSString *const textFieldTextBeginEditingNotification;
    UIKIT_EXTERN NSString *const textFieldTextEndEditingNotification;
    正確操做:
    UIKIT_EXTERN NSString *const UITextFieldTextDidBeginEditingNotification;
    UIKIT_EXTERN NSString *const UITextFieldTextDidEndEditingNotification;

    ps:這裏面須要注意的是變量名儘可能不要使用縮寫,如咱們常常能夠看到不少開發者習慣於把根視圖控制器寫成rootVC或者mainVC等等,而系統給咱們提供的倒是完整的命名:self.window.rootViewController,假如系統給咱們提供的是self.window.rootVC這種形式,以及其餘命名方式也這樣以非專業詞彙的縮寫命名,相信不少開發者會看的一頭霧水。

 

4.宏命名規則

 

一般會把單詞的全部字母大寫,目的是爲了告訴開發者這是一個宏,而不是一個普通的變量,固然若是宏的名稱若是由多個單詞組成,一般是每一個單詞之間使用下劃線分割開,如:「JXL_ABC_DEF」這種形式。

 

5.方法命名規則

大部分方法能夠分爲兩類:要什麼 和 作什麼

  • 要什麼表示取得某個對象,要以名詞做爲方法的開頭如:「string」,「data」,「image」等,案例:

    - (NSRange)rangeOfString:(NSString *)searchString;  // 代表獲取一個range
    - (UIImage *)imageNamed:(NSString *)name;

         

       

  • 作什麼表示執行某種操做,要以動詞做爲方法開頭
  • (NSArray<ObjectType> *)sortedArrayUsingSelector:(SEL)comparator;  // 代表目的是排序
    - (void)setUpNavBar

 

  • 若是該方法須要參數,每一個參數前最好添加參數提示。以下面兩種代碼對比。

    - (instancetype)init:(CGRect)frame;  // 糟糕的方法命名
    - (instancetype)initWithFrame:(CGRect)frame;  // 好的方法命名

 

  • 若是該方法須要多個參數,不能使用and這個單詞鏈接參數

    1
    - (instancetype)initWithWidth:(CGFloat)width height:(CGFloat)height;
  • 一些表明過程監聽的方法可能以「誰執行什麼過程」這種形式命名,且動做發生以前一般使用「Will」,發生以後使用「Did」,詢問是否發生使用「Should」。案例:
    1
    2
    3
    - (nullable NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath;  //將要選擇這一行cell
    - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath; //已經選擇這一行cell
    - (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath NS_AVAILABLE_IOS(6_0); //是否高亮(選擇這一行cell)

 

  • 回調方法第一個參數是調用者
    1
    - (BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:(NSDictionary *)launchOptions;
    - (void)buttonTapped:(UIButton*)sender;

 

  • 類目方法:若是咱們在編碼中使用類目給一些系統的類拓展方法,那麼推薦給這些方法添加前綴,目的很簡單,就是避免和這個類的私有方法或者未來系統可能拓展的方法出現方法名相同的衝突。如我當初在寫夜間模式的demo的時候給UIView寫了一個類目,拓展的方法名我使用了姓名的縮寫「jxl」做爲方法的前綴:
  • (void)jxl_setDayMode:(DAY_AND_NIGHT_MODE_BLOCK)dayMode nightMode:(DAY_AND_NIGHT_MODE_BLOCK)nightMode;   

 

 

  • 返回BOOL值得方法加前綴is,has

    1
    2

    - (BOOL)isEqualToString:(NSString *)aString;
    - (BOOL)hasPrefix:(NSString *)aString;

 

 

二 代碼編寫規範

1.引用頭文件

類的頭文件儘可能不要引用其餘頭文件,無需知道類的內部細節使用@class便可

 

2.使用類型常量替換#define預處理指令

在編寫代碼時,咱們經常使用#define去定義一個宏。咱們定義一個播放動畫的時間爲常量,可能這樣定義:

#define ANIMATION_DURATION 1.0

上述預處理指令會把源代碼中的 ANIMATION_DURATION 替換爲1.0,能夠達到效果,不過這樣定義是存在問題的,定義出來的常量沒有類型信息,沒法一眼看出表明的是一個時間,可讀性差,並且若是把這個宏定義放在頭文件中的話,那麼引入了這個頭文件的代碼,其 ANIMATION_DURATION 都會被替換,若是有人定義了常量值,這將致使應用程序中的常量值不一致。一種更好的處理方案是使用類型常量替換掉相應的#define預處理指令。

  • 外部不可見:

    .m文件中:

static const NSTimeInterval kAnimationDuration = 1.0;// 命名規則:不被外部訪問時 k+變量名

static修飾:意味着僅在此編譯單元(.m文件)中可見;const修飾:若是試圖修改值,編譯器就會報錯;static const:兩者都使用,編譯器的處理效果和#define同樣,把遇到的變量替換爲常值。

  • 外部可見:
    有些時候是須要向外部公開某個常量的,For example,在使用通知中心的時候,你須要向其餘對象派發通知,監聽者須要知道監聽的事件,這個事件的名稱咱們一般寫成一個外界可見的常值變量,這樣的話,監聽者無需知道實際字符串的值,只須要以常值變量來做爲本身監聽的事件名稱,系統的 UIApplicationDidEnterBackgroundNotification, UIApplicationWillEnterForegroundNotification等都是這樣作的:
    在.h文件中:

UIKIT_EXTERN NSString *const MyClassNameActionNameNotification; // 命名規則:類名+事件名+Notification   

ps:從右至左解讀,「一個常量,而這個常量是一個指針,指向NSString對象」。
在.m文件中:

NSString *const MyClassNameActionNameNotification = @"MyClassNameActionNameNotification";
  • 本例中的寫法爲:
    在.h文件中:

UIKIT_EXTERN const NSTimeInterval MyClassNameAnimationDuration;// 命名規則:類名+變量名

在.m文件中:

const NSTimeInterval MyClassNameAnimationDuration = 0.3;

 

3.用枚舉表示設置或狀態

當咱們想要表示某一種設置的多個選項或者多種狀態時,推薦使用枚舉。枚舉的意義原本就是將一些表示某一種設置或者狀態的數字轉化成方便開發者閱讀的形式,極大的提升了可讀性。以上面其餘的命名規則話題中提到過的枚舉爲例:

typedef NS_ENUM(NSInteger, UIViewAnimationTransition) { 
        UIViewAnimationTransitionNone,   // 枚舉值命名
        UIViewAnimationTransitionFlipFromLeft, 
        UIViewAnimationTransitionFlipFromRight, 
        UIViewAnimationTransitionCurlUp, 
        UIViewAnimationTransitionCurlDown, 
    };

 

這個枚舉含有5個值,分別表示了5種動畫狀態,若是系統以「0」,「1」,「2」等這樣的數字來表示狀態的話,做爲開發者想要知道每一個數字表明什麼樣的動畫效果只能一個一個的去測試,而且須要記住每個數字表明什麼狀態,方便之後使用,那麼我相信絕大多數開發者都會瘋掉的。

 

4.協議的簽定格式

協議的簽定推薦下面這種寫法,這種寫法的好處是你簽定了什麼協議一目瞭然,並且後面填寫註釋看起來也會更加舒服。

@interface FooViewController ()
<
    UITableViewDataSource, // 你的註釋
    UITableViewDelegate // 你的註釋
>
@end

 

 

5.代碼整理

爲了讓你的代碼更整潔,你須要將你的代碼作好歸類整理,例如一個ViewController實現文件裏的代碼多是這樣:

#pragma mark - Life Cycle

// Methods...
#pragma mark - UITableViewDataSource

// Methods...

#pragma mark - UITableViewDelegate

// Methods...

#pragma mark - CustomDelegate

// Methods...

#pragma mark - Private Methods

// Methods…(.m中聲明)

#pragma mark - Public Methods

// Methods…(.h中聲明)

#pragma mark - Getters and Setters

// Methods...

#pragma mark - Notification

//Methods...

#pragma mark - Event Response

// Methods...

#pragma mark - Request Methods

//Methods...

 

6.實例變量聲明時變量名前面加下劃線「_」,局部變量不用加

錯誤示範:
@implementation ViewController
{
    UIButton *authCodeBtn;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *_loginBtn = [[UIButton alloc] init];
}

好的習慣:
@implementation ViewController
{
    UIButton *_authCodeBtn;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *loginBtn = [[UIButton alloc] init];

}

 

7.場景需求:在繼承中,凡是要求子類重寫父類的方法必須先調用父類的這個方法進行初始化操做;建議:父類的方法名後面加上NS_REQUIRES_SUPER; 子類重寫這個方法就會自動警告提示要調用這個super方法,示例代碼

// 注意:父類中的方法加`NS_REQUIRES_SUPER`,子類重寫纔有警告提示

- (void)prepare NS_REQUIRES_SUPER;

8.成員變量 setter&getter

一些剛剛入門的iOS開發者老是糾結在給屬性賦值或者取值的時候,到底是直接操做成員變量仍是經過使用setter或者getter進行賦值取值操做,關於這個問題不少人的觀點也不相同,從性能上來講,因爲Objective-C的消息機制使用setter和getter效率要比直接操做成員變量的效率低,從內存方面來講,setter和getter都有內存的處理,因此使用起來更安全,一個折中的辦法是除Lazy Loading對象外,賦值時使用setter,取值時直接操做成員變量,這樣既保障了內存的安全,又提高了效率,並且一般咱們的取值操做又多於賦值操做。

 

儘量使用點語法訪問屬性,可是訪問其餘實例對象使用括號

1
2
3
4
5

view.backgroundColor = [UIColor redColor];
[UIApplication sharedApplication].delegate; //推薦

[view setBackgroundColor:[UIColor redColor]];
UIApplication.sharedApplication.delegate; //不推薦

 

 

9.定義屬性儘量寫全參數 

1
@property (nonatomic, readwrite, copy) NSString *name;
  • 若是是內部使用的屬性, 需定義成該類的私有屬性(寫在.m文件的class extension裏)
  • 對於擁有Mutable子類型的對象, 例如NSString NSArray NSDictionary NSDictionary, 必定要定義成copy屬性 
  • 儘可能不要暴露mutable類型的對象在public interface, 建議在.h定義一個Inmutable類型的屬性, 而後在.m的get函數裏面返回一個內部定義的mutable變量
  • 不要出現混合聲明,儘量都使用@property聲明

10.使用字面量語法替換等價方法

字面量語法其實是一種「語法糖」(syntactic sugar),以一種很是簡單快捷的方式能建立對象,使咱們開發者編程更高效,更愉悅。目前Objective-C支持的字面量語法的類有NSString,NSNumber, NSArray, NSDictionary。使用字面量語法的好處:

  • 使用字面量語法能夠縮減源代碼長度,沒有多餘語法成分,提升可讀性;

  • 在使用字面量語法建立數組時,若是數組元素對象中有nil,則會拋出異常,其效果等於先建立一個數組,而後把方括號內的全部對象都加到這個數組中。拋出的異常會是這樣:

***Terminating app due to uncaught exception
‘NSInvalidArgumentException', reason:’***
-[__NSPlaceholderArray initWithObjects:count:] : attempt to
insert nil object from objects[0]'

 

案例:

NSArray *arrayA = [NSArray arrayWithObjects:object1object2object3, nil];

NSArray *arrayB = @[object1object2object3];

NSNumber *isHide = @NO;
NSNumber *errorCode = @404;

針對上面代碼進行分析,若是object2=nil;,arrayA數組能夠建立,但只有object1一個對象,由於「+ (instancetype)arrayWithObjects:」方法會一次處理各個參數,直到發現nil爲止,而arrayB會拋出異常,這個特性使咱們更容易發現程序中存在的問題,提升了安全性。ps:字典跟數組同樣,一旦有值爲nil,也會拋出異常,並且建立時的「鍵」「值」順序和咱們正常說的「鍵值」順序同樣(正常初始化爲「值」「鍵」),便於閱讀。

使用字面量語法的缺點:使用字面量建立都是不可變對象,若是想建立可變對象須要複製一份:

NSMutableArray *mutableArray = [@[@1, @2, @3] mutableCopy];

 

 

11.判斷nil或者YES/NO

1
2
3
4
5
6
7
8
if (obj) 
  { 
    //... 
  }
if (!obj) 
  { 
    //... 
  }

 

 

12.BOOL類型賦值 

錯誤示例:
Bool isAdult;
if (age > 18){
  isAdult = YES;
else {
  isAdult = NO;
}
//
好的習慣
Bool isAdult;
isAdult = age > 18;

1


BOOL isAdult = age > 18;

13. 複雜的條件判斷

若是判斷較爲複雜,儘量寫到一個方法裏 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16


if ([self canDeleteAccount:account])
  { //... }
/**
 method
*/
- (BOOL)canDeleteAccount:(account)
{
        if (account.balance == 0  || account.owner.isDead == YES || account.isCancel == YES)
  {
         return YES;
        }
 else
       {
         return NO;
 }
}

14.嵌套判斷

錯誤示例:
if(userName.length){
     if (passWord.length) {
            //能夠登陸
        }
}
好的習慣:
if(!userName.length){ return; };
if(!passWord.length){ return; };

1

2
3


if (!user.account) return NO;
if (!user.password) return NO;
return YES;

15.加載xib

加載xib名稱使用 NSStringFromClass()

1


[self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([UserListCell class]) bundle:nil] forCellReuseIdentifier:ID];
 

16.判斷if書寫方式 

1
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
    if (indexPath.row == 0) return 40;
    if (indexPath.row == 1) return 50;
    if (indexPath.row == 2) return 60;
    return 44;
}

 

 

17.對於參數不少的函數,參數各佔一行,並以冒號對齊,如:

1
- (void)exampleFunctionWithPara:(int)para1
                    anotherPara:(int)para2
                   theThirdPara:(int)para3
                  theFourthPara:(int)para4
{
    ...
}
1


[self exampleFunctionWithPara:1
                  anotherPara:2
                 theThirdPara:3
                theFourthPara:4
                 theFifthPara:5];
 

18.運算符號間要留有合適的間隔

好的習慣:
sum = value1 + value2;//瞬間 高大上
UILable *lbl = [[UILable alloc] init] ;

 

19.字典構造時的注意點 : 先後要留有一個空格

錯誤示範:
 NSDictionary *attributs = @{                       
    NSForegroundColorAttributeName:[UIColor orangeColor],
    NSFontAttributeName:[UIFont systemFontOfSize:12]
    };
正確示例:
  NSDictionary *attributs = @{                       
    NSForegroundColorAttributeName : [UIColor orangeColor],
    NSFontAttributeName : [UIFont systemFontOfSize:12]
    };

 

 

 

20.對一些相同的東西避免寫死,特別是控件的frame 不便於修改

錯誤示例:
 UITextField *phoneTf = [[UITextField alloc] initWithFrame:CGRectMake(10010040)];
 [self.view addSubview:phoneTf];
 UITextField *passwordTf = [[UITextField alloc] initWithFrame:CGRectMake(1011010040)];
 [self.view addSubview:passwordTf];
//
正確的方式:
 CGFloat margin = 10;
 CGFloat width = 100;
 CGFloat height = 40;
 UITextField *phoneTf = [[UITextField alloc] initWithFrame:CGRectMake(margin, 0, width, height)];
 [self.view addSubview:phoneTf];
 UITextField *passwordTf = [[UITextField alloc] initWithFrame:CGRectMake(margin, CGRectGetMaxY(phoneTf.frame) + margin, width, height)];
 [self.view addSubview:passwordTf];

 

21.避免循環引用

若是【block內部】使用【外部聲明的強引用】訪問【對象A】, 那麼【block內部】會自動產生一個【強引用】指向【對象A】

若是【block內部】使用【外部聲明的弱引用】訪問【對象A】, 那麼【block內部】會自動產生一個【弱引用】指向【對象A】

__weak typeof(self) weakSelf = self;
dispatch_block_t block = ^{
   [weakSelf doSomething]; // weakSelf != nil
// preemption, weakSelf turned nil
[weakSelf doSomethingElse]; // weakSelf == nil
};
最好這樣調用:
__weak typeof(self) weakSelf = self;
myObj.myBlock = ^{

__strong typeof(weakSelf) strongSelf = weakSelf;

if (strongSelf) {
     [strongSelf doSomething]; // strongSelf != nil
// preemption, strongSelf still not nil(搶佔的時候,strongSelf 仍是非 nil 的)
[strongSelf doSomethingElse]; // strongSelf != nil }
else { // Probably nothing... return;
}
};

weakSelf是爲了block不持有self,避免循環引用,而再聲明一個strongSelf是由於一旦進入block執行,就不容許self在這個執行過程當中釋放。block執行完後這個strongSelf會自動釋放,沒有循環引用問題。

22.建議:

用CGSizeZero 代替 CGSizeMake(0,0);

CGRectZero代替CGRectMake(0, 0, 0, 0);

CGPointZero代替CGPointMake(0, 0)

 

23.在導航控制中,或它的子控制器,設置導航欄的標題應該用self.navigationItem.title = @「標題」而不建議self.title = @「標題」;

相關文章
相關標籤/搜索