標籤:objective-c起源 static const 枚舉
Objective-C通過一套全新的文法,在C語言基礎上添加了物件導向特性。OC的文法中頻繁使用中括弧([ ]),而且不吝於寫出極長的方法名,這通常令許多人覺得此語言較為冗長。這是這樣寫出來的代碼非常易讀,只是C++和Java程式員不太適應。
OC語言學起來很快,但有很多微妙細節需要注意,而且還有許多容易為人所忽略的特性。另一方面,有些開發人員並未完全理解或是容易濫用某些特性,導致寫出來的代碼難以維護,難以調試。本章講解基礎知識,後續各章語言及其相關架構的各個特定話題。
第1條:瞭解OC語言的起源
OC與C++,Java等物件導向語言類似,不過很多方面有差別。若是用過另一種物件導向語言,那麼就能理解OC所用的許多範式與模板了。然而文法上也許會顯得陌生,因為OC使用訊息結構(messaging structure)而非函數調用(function calling)。OC由SmallTalk演化而來,後者是訊息型語言的鼻祖。訊息與函數調用之間的區別看上去就像這樣:
//Messaging (OC)Object *obj = [Object new];[obj perfromWith:parameter1 and:parameter2];//Function calling(C++)Object *obj = new object;obj->perform(parameter1,parameter2);
關鍵區別在於:使用訊息結構的語言,其運行時所應執行的代碼由運行環境決定;而使用函數調用的語言,由編譯器決定。如果範例代碼中調用函數是多態的,那麼在運行時就要按照“虛函數表”(virtual table)來尋找到底應該執行哪個函數。而採用訊息結構的語言,不論是否多態,總是在運行時才會去尋找所要執行的方法。實際上,編譯器甚至不關心接受訊息的對象是何種類型。接受訊息的對象問題也要在運行時處理,其過程叫做“動態綁定”(dynamic binding)見第11條
OC的重要工作都由“運行期組件”(runtime component)而非編譯器來完成。使用OC的物件導向特性所需的全部資料結構及函數都在運行期組件裡面。舉例來說,運行期組件中含有全部記憶體管理方法。運行期組件本質上就是一種與開發人員所編代碼相連結的“動態庫”(dynamic library),其代碼能把開發人員編寫的所有程式戰粘和起來。這樣,只需更新執行期組件,即可提升應用程式效能。
OC是C的超集,所以C語言中的所有功能在編寫OC代碼時依然適用。
OC只能在堆上聲明變數,不能在棧上聲明(CGRect可以聲明在棧上,因為CGRect是C結構體),OC將堆記憶體管理抽象出來了。不需要malloc及free來分配或釋放對象所佔記憶體。OC運行期環境把這部分工作抽象為一套記憶體管理架構,名叫“引用計數”,見第29條
第2條:在類的標頭檔中盡量少引入其他標頭檔
與C和C++一樣,OC使用標頭檔與“實現檔案”來區隔代碼。用OC語言編寫任何類幾乎都要引入Foundation.h。
盡量在標頭檔中使用類聲明,防止迴圈標頭檔引用,過多的引用標頭檔也會增加編譯時間。
第3條:多用字面量文法,少用與之等價的方法
舉例:
NSNumber *someNumber = @1;NSNumber *intNumber = @1;NSNumber *floatNumber = @2.5f;NSArray * animals = @[@"cat",@"dog",@"mouse",@"badger"];NSString *dog = animals[1];//----------------------id object1 = /*......*/;id object2 = /*......*/;id object3 = /*......*/;NSArray* arrayA = [NSArray ArrayWithObjects:object1,object2,object3,nil];NSArray* arrayB = @[object1,object2,object3];//如果object2 = nil那麼arrayB拋出異常,array中只有object1和object2。//----------------------NSDictionary *personData = @{@"firstName":@"Matt",@"lastName":@"Galloway",@"age":@28};
第4條:多用類型常量,少用#define預先處理指令
編寫代碼經常需要定義常量。例如:
#define ANIMATION_DURATION 0.3
#define會編譯期簡單替換文本,會造成很多不必要的麻煩。要像解決此問題,應該設法利用編譯器的某些特性才行。比如:
static const NSTimeInterval kAnimationDuration = 0.3;
這種方式定義常量包含了類型資訊,且最好將這個聲明放到.m檔案中。static const的聲明不應該出現在標頭檔裡。因為OC沒有命名空間,所以那樣就等於聲明了一個全域變數。static修飾表示該變數僅定義在此.m檔案中(一個編譯單元),如果不加static,則編譯器會為他建立一個“外部符號”(external symbol)。此時若另外一個.m檔案也有了同名變數,就會造成符號重複(duplicate symbol)。
實際上,如果一個變數既聲明為static,又聲明為const,那麼編譯器根本不會建立符號,而是會像#define一樣預先處理指令一樣,把所有遇到的變數都替換為常量。
有時候需要對外公開某個常量,例如:
//in the header fileextern NSString *const EOCStringConstant;//in the implement fileNSString* const EOCStringConstant = @"VALUE";
EOCStringConstant是一個常量指標,編譯器會看到標頭檔中的extern關鍵字,這個關鍵字是要告訴編譯器,在全域符號表中將會有有一個名叫EOCStringConstant的符號。當連結成二進位檔案之後,肯定能找到這個常量。因為符號要放在全域符號表裡,所以命名常量時需謹慎。
本節要點:
● 不要用預先處理指令定義常量。即使有人重新定義了常量值,編譯器頁不會產生警告資訊,這將導致程式中常量不一致。
● 在實現檔案中使用static const來定義“只在編譯單元內可見的常量”(translation-unitspecific constant)。此類常量不在全域符號表中。
● 在標頭檔中使用extern來聲明全域常量,並在實現檔案中定義其值。
第5條:用枚舉表示狀態、選項、狀態代碼
直接舉例
typedef NS_ENUM(NSUInteger,EOCConnectionState){ EOCConectionStateDisconnected, EOCConectionStateConnecting, EOCConectionStateConnected,};
● 用NS_ENUM與NS_OPTIONS宏來定義枚舉類型,並指明底層資料類型。這樣做可以確保枚舉是開發人員所選的底層資料類型實現出來的,而不會採用編譯器所選的類型。
其餘章節點擊這裡