【Effective Objective-C 2.0讀書筆記】第一章:熟悉Objective-C

Objective-C通過一套全新語法,在C語言基礎上添加了面向對象特性。Objective-C的語法中頻繁使用方括號,且不吝於寫出極長的方法名。

第1條:瞭解Objective-C語言的起源

所有Objective-C語言的對象都必須像這樣聲明:NSString *someString = @"the string";,因爲對象所佔內存總是分配在“堆空間”(heap space)中,而絕不會分配在“棧”(stack)中。不能在棧中分配Objective-C對象。

如果此時再聲明一個變量指向同一個NSString對象,即NSString *anotherString = someString;,則這兩個變量都是NSString *型,這說明當前“棧幀”(stack frame)裏分配了兩塊內存,每塊內存的大小都能容納一枚指針(在32位架構的計算機上是4字節,64位計算機上是8字節)。這兩塊內存裏的值都一樣,就是NSString實例的內存地址。

分配在堆中的內存必須直接管理,而分配在棧上用於保存變量的內存則會在其棧幀彈出時自動清理。

Objective-C將堆內存管理抽象出來了。不需要用mallocfree來分配或釋放對象所佔內存。Objective-C運行期系統將這部分工作抽象爲一套內存管理架構,名爲“引用計數”。

在Objective-C代碼中,有時會遇到定義裏不含*的變量,它們可能會使用“棧空間”(stack space)。這些變量所保存的不是Objective-C對象。如CoreGraphics框架中的CGRect,整個系統框架都在使用這種結構體,因爲若改用Objective-C對象的話,性能會受影響。與創建結構體相比,創建對象還需要額外開銷,例如分配及釋放堆內存等。如果只需保存intfloatdoublechar等非對象類型,那麼通常使用CGRect這種結構體就可以了。

第2條:在類的頭文件中儘量少引入其他頭文件

每次在頭文件中引入其他頭文件之前,都要先問問自己這樣做是否確有必要。如果可以用“向前聲明”取代引入,那麼就不要引入。若因爲要實現屬性、實例變量或者要遵從協議而必須引入頭文件,則應儘量將其移至“class-continuation分類”中。這樣做不僅可以縮減編譯時間,而且還能降低彼此依賴程度。

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

要點:

應該使用字面量語法來創建字符串、數值、數組、字典。與創建此類對象的常規方法相比,這樣做更簡明扼要。

應該通過取下標操作來訪問數組下標,或字典中的鍵所對應的元素。

用字面量語法創建數組或字典時,若值中有nil,則會拋出異常。因此,務必確保值裏不含nil

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

consts vs defines

consts are type safe and respect scope with syntax highlighting.
defines are the exact opposite.

const定義常量從彙編的角度來看,只是給出了對應的內存地址,而不是象#define一樣給出的是立即數,所以,const定義的常量在程序運行過程中只有一份拷貝,而#define定義的常量在內存中有若干個拷貝。

編譯器通常不爲普通const常量分配存儲空間,而是將它們保存在符號表中,這使得它成爲一個編譯期間的常量,沒有了存儲與讀內存的操作,使得它的效率比宏定義要高。

實例:

// 定義“只在編譯單元內可見的常量”
// In the implementation file
// 局部常量:用static修飾後,不能提供外界訪問
static const NSTimeInterval kAnimationDuration = 0.3; // 變量不可修改
// 根據const修飾的位置設定能否修改
static const NSString *kStringConstant1 = @"VALUE1"; // 變量可以被修改
static NSString *const kStringConstant2 = @"VALUE2"; // 變量不可修改

// 聲明全局變量
// In the header file
extern NSString *const EOCStringConstant;
// 定義全局變量
// In the implementation file
NSString *const EOCStringConstant = @"VALUE";

const修飾位置不同,代表什麼?

  1. const NSString *HSCoder = @”HSCoder”;
    “*HSCoder”不能被修改, “HSCoder”能被修改

  2. NSString const *HSCoder = @”HSCoder”;
    “*HSCoder”不能被修改, “HSCoder”能被修改

  3. NSString * const HSCoder = @”HSCoder”;
    “HSCoder”不能被修改,”*HSCoder”能被修改

注意:1和2其實沒什麼區別。

結論:

const右邊的總不能被修改。

const

const不可變原則:那就是它右邊是什麼, 什麼就不可變。

用const修飾函數的形參, 則能提高代碼的安全性, 同時減少程序員之間的溝通成本。

#define

宏多用於條件編譯, 如需要對於不同的情況執行不同的代碼塊, 則可以使用宏的條件編譯來進行判斷。

宏能做到const不能辦到的事.1
宏能定義函數

OC的單例模式用到宏

宏還能根據傳入的參數生成字符串, 如下:
#define kStringCat(x, y) #x#y
#define kToString #x

在Objective-C中, 隨處可見const常量, 所以大家應該大膽地使用const, 它會帶給你大大的益處. 同時, 只要某個數據是定義之後永遠都不需要也不能修改的, 請使用const!

FOUNDATION_EXPORT vs extern

The first one compiles different for C and C++. It basically means `extern`, but in C++ will add the "C" flag.
#if defined(__cplusplus)
#define FOUNDATION_EXTERN extern "C"
#else
#define FOUNDATION_EXTERN extern
#endif

#if TARGET_OS_WIN32

    #if defined(NSBUILDINGFOUNDATION)
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllexport)
    #else
        #define FOUNDATION_EXPORT FOUNDATION_EXTERN __declspec(dllimport)
    #endif

    #define FOUNDATION_IMPORT FOUNDATION_EXTERN __declspec(dllimport)

#else
    #define FOUNDATION_EXPORT  FOUNDATION_EXTERN
    #define FOUNDATION_IMPORT FOUNDATION_EXTERN
#endif

要點:

不要用預處理指令定義常量,因爲這樣定義的常量不含類型信息,編譯器只是會在編譯前據此執行查找和替換操作。即使有人重新定義了常量值,編譯器也不會產生警告信息,這將導致應用程序中的常量值不一致。

在實現文件中使用static const來定義“只在編譯單元內可見的常量”(translation-uinit-specific constant)。由於此類常量不在全局符號表裏中,因此無須爲其名稱加前綴。

在頭文件中使用extern來聲明全局變量,並在相關實現文件中定義其值。這種常量會出現在全局符號表中,所以其名稱應加以區隔,通常用與之相關的類名作前綴。

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

要點:

NS_ENUMNS_OPTIONS宏來定義枚舉類型,並指明其底層數據類型。這樣做就可以確保枚舉是用開發者所選的底層數據類型實現出來的,而不會採用編譯器所選的類型。

在處理枚舉類型的switch語句中,不要實現default分支。這樣的話,加入新枚舉之後,編譯器就會提示開發者:switch語句並未處理所有枚舉。


  1. 15分鐘弄懂 const 和 #define http://ios.jobbole.com/85007/
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章