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將堆內存管理抽象出來了。不需要用malloc
及free
來分配或釋放對象所佔內存。Objective-C運行期系統將這部分工作抽象爲一套內存管理架構,名爲“引用計數”。
在Objective-C代碼中,有時會遇到定義裏不含*的變量,它們可能會使用“棧空間”(stack space)。這些變量所保存的不是Objective-C對象。如CoreGraphics框架中的CGRect
,整個系統框架都在使用這種結構體,因爲若改用Objective-C對象的話,性能會受影響。與創建結構體相比,創建對象還需要額外開銷,例如分配及釋放堆內存等。如果只需保存int
、float
、double
、char
等非對象類型,那麼通常使用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修飾位置不同,代表什麼?
const NSString *HSCoder = @”HSCoder”;
“*HSCoder”不能被修改, “HSCoder”能被修改NSString const *HSCoder = @”HSCoder”;
“*HSCoder”不能被修改, “HSCoder”能被修改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_ENUM
與NS_OPTIONS
宏來定義枚舉類型,並指明其底層數據類型。這樣做就可以確保枚舉是用開發者所選的底層數據類型實現出來的,而不會採用編譯器所選的類型。在處理枚舉類型的
switch
語句中,不要實現default
分支。這樣的話,加入新枚舉之後,編譯器就會提示開發者:switch
語句並未處理所有枚舉。
- 15分鐘弄懂 const 和 #define http://ios.jobbole.com/85007/ ↩