觀“編寫高質量iOS與OC 代碼的52個有效方法”有感(一)· 熟悉Objective-C

程序猿.jpg

1、了解Objective-C語言的起源

  • OC語言使用“消息結構” 而非“函數調用”
    消息結構:
    Object *obj = [Object new];
    [obj performWith:parameter1 and:parameter2];
    特點:運行時的代碼由運行環境來決定。

函數結構:
Object *obj = new Object;
obj->perform(parameter1,parameter2);
特點:運行時的代碼由編譯器來決定。

  • OC是C的“超集”,所以C語言的所有功能在編寫OC代碼時依然適用!
    OC中的內存模型:
    NSString *nameString = @“小伙子”;
    上面就是說:聲明了一個名為 nameString 的變量 ,其類型為NSString *
    也就是說,此變量為指向NSString 的指針。當然了不止NSString這樣聲明,所有Objective-C的對象都必須這樣聲明。原因:對象所占內存總是分配在“堆空間”中,而不會分配到“棧”上。
  • 在OC中,你還會碰到一些不需要 *的變量。他們可能會使用“棧空間” 這些變量保存的不是Objective-C對象。舉個例子
struct CGRect {
    CGPoint origin;
    CGSize size;
};
typedef struct CGRect CGRect;

系統框架都在使用結構體。因為使用Objective-C來做的話會影響性能(創建對象、分配內存、釋放內存)。如果只是保存int、float、double、char這些“非對象類型” 通常使用CGRect結構體就可以了。

2、在類的頭文件中盡量少引入其他頭文件

  • 現象:
    Objective-C創建文件分為.h(頭文件)和.m(實現文件)
    假如在某個頭文件中引入一些根本用不到的頭文件。那么程序也會引入頭文件的內容。這樣會增加編譯時間。
  • 建議:
    除非確實有必要,否則不要引入頭文件。
    應該在實現文件中引入要使用的頭文件,降低類之間的耦合。

3、多用字面量語法,少用與之等價的方法

  • 實現
    語法創建:
    //多種類型的數值
    NSNumber *number = [NSNumber numberWithInt:1];
    
    //數組創建
    NSArray *array = [NSArray arrayWithObjects:@"1",@"2",@"3", nil];
    
    //字典的創建
    NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:@"Mary",@"name",@"Secr",@"name1" ,nil];

字面量創建

//數值創建
    NSNumber *number = @1;
    int a = 1;
    float b = 4.23f;

    //數組創建
    NSArray *array = @[@"1",@"2",@"3"];
    
    //字典的創建
    NSDictionary *dictionary = @{@"Mary":@"name",@"Secr":@"name1"};
  • 結論
    • 使用字面量語法創建字符串、數值、數組、字典。與創建此類對象的常規方法相比,這么做更加簡明扼要。
    • 用字面量創建數組或者字典時,若值中有nil,則會拋出異常。因此,務必確保值里不含nil。

4、多用類型常量,少用#define預處理指令

  • 編寫代碼時候經常要定義常量。
    例如一個動畫的播放時間
    #define ANIMATION_DURATION 0.3
    預處理指令會把源代碼中的ANIMATION_DURATION字符串替換為0.3。
    but、這樣定義出來的常量沒有類型信息。
    • 名字(duration)這個詞看上去與時間有關,但是代碼中并沒有標出。
    • 假如其他文件中有ANIMATION_DURATION ,也會一律替換成0.3
  • 可以這樣解決:
    static const NSTimeInterval kAnimationDuration = 0.3;
    • 包括了類型信息、有助于編寫開發文檔、讓接手代碼的人更易讀。
    • 試圖修改const修改的變量,那么編譯器會報錯。
  • 注意:
    • 常量的命名規則:
      常量局限于實現文件之內,在前面加字母k。
      若是常量在類之外可見,那么要以類名為前綴。比如SCViewClassAnimationDuration。
  • 外界可見的常值變量:
    • 這種常量放在“全局符號表”中,以便可以在定義該常量的編譯單元之外使用!
      .h中
extern NSString *const SCNameStringConstant;

.m中

NSString *const SCNameStringConstant = @"Peter";
  • 原理:
    編譯器看到頭文件中的extern關鍵字后,就知道在全局符號表中有一個名叫 SCNameStringConstant 的符號。
    編譯器無需查看其定義,允許代碼使用此常量。因為它知道肯定能找到這個常量。
  • 注意:
    此常量必須要定義且只能定義一次。通常是定義在與聲明常量的頭文件相關的實現文件里。鏈接器會把此目標文件和其他目標文件相鏈接,形成二進制文件,凡是用到SCNameStringConstant的地方都能解析。
    因為符號要放在全局符號表里,所以命名的時候一定要注意。最好是用與之相關的類名做前綴!

5、用枚舉表示狀態、選項、狀態碼

  • 枚舉:enum
    系統框架中頻繁用到此類型:
    比如創建UIButton時候
typedef NS_ENUM(NSInteger, UIButtonType) {
    UIButtonTypeCustom = 0,                         // no button type
    UIButtonTypeSystem NS_ENUM_AVAILABLE_IOS(7_0),  // standard system button

    UIButtonTypeDetailDisclosure,
    UIButtonTypeInfoLight,
    UIButtonTypeInfoDark,
    UIButtonTypeContactAdd,
    
    UIButtonTypeRoundedRect = UIButtonTypeSystem,   // Deprecated, use UIButtonTypeSystem instead
};

枚舉只是一種常量命名方式。每種狀態用一個易讀的值來表示,這樣更方便理解。編譯器會為枚舉分配一個獨有的編號,從0開始,每個枚舉遞增1。

  • 比如狀態一般分為“OK”和“Error”。只能選擇其中一種,不可能出現2種。
    我們可能通過枚舉這樣做
typedef enum _TTGState {
    TTGStateOK  = 0,
    TTGStateError,
    TTGStateUnknow
} TTGState;

  //指明枚舉類型
TTGState state = TTGStateOK;

用的時候就這樣:

 - (void)dealWithState:(TTGState)state {
   switch (state) {
       case TTGStateOK:
           //...
           break;
       case TTGStateError:
           //...
           break;
       case TTGStateUnknow:
           //...
           break;
     }
  }
     ```
- 選項:就是說一個“選項變量”的類型要能夠同時表示一個或多個組合的選擇。
比如說iPhone 可以支持多個方向。

//方向,可同時支持一個或多個方向
typedef enum _TTGDirection {
TTGDirectionNone = 0,
TTGDirectionTop = 1 << 0,
TTGDirectionLeft = 1 << 1,
TTGDirectionRight = 1 << 2,
TTGDirectionBottom = 1 << 3
} TTGDirection;

好處就是我們選項變量可以如下表示:

//用“或”運算同時賦值多個選項
TTGDirection direction = TTGDirectionTop | TTGDirectionLeft | TTGDirectionBottom;

//用“與”運算取出對應位
if (direction & TTGDirectionTop) {
NSLog(@"top");
}
if (direction & TTGDirectionLeft) {
NSLog(@"left");
}
if (direction & TTGDirectionRight) {
NSLog(@"right");
}
if (direction & TTGDirectionBottom) {
NSLog(@"bottom");
}

一般來說,我們不能指定枚舉變量的實際類型是什么,就是說,我們不知道枚舉最后是int型,還是其他的什么類型。但是從C++ 11開始,我們可以為枚舉指定其實際的存儲類型。
      enum TTGState : NSInteger {/*...*/};
Foundation框架中定義了一些輔助的宏,這些宏具備了向后兼容的能力(判斷編譯器是否支持新的枚舉特性,不支持就用老的語法來定義枚舉)。

- 在iOS程序中,最好所有的枚舉都用“NS_ENUM”和“NS_OPTIONS”定義,保證統一。

### 接下來也將會繼續整理。如果覺得有用請點個喜歡!
### 您的支持將是我繼續寫作的動力!謝謝。

[觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(一)· 熟悉Objective-C](http://www.lxweimin.com/p/0876e60d3160)
[觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(二)· 對象、消息、運行時](http://www.lxweimin.com/p/1aac4fb98888)
[觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(三)· 接口與API設計](http://www.lxweimin.com/p/5d9e61db2a01)
[觀“編寫高質量iOS與OC X代碼的52個有效方法”有感(四)· 協議與分類](http://www.lxweimin.com/p/0944e1e276ff)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容