編寫高品質的OC代碼--------熟悉Objective-C
來源:互聯網
上載者:User
第1條:瞭解Objective-C語言的起源
1、Objective-C的訊息結構
Objective-C使用“訊息結構”而非“函數調用”。訊息結構與函數調用的關鍵區別在於:使用訊息結構的語言,其運行時所應執行的代碼由運行環境來決定;而使用函數調用的語言,由編譯器決定。如果函數調用是多態的,那麼在運行時就要按照“虛方法表”來查出到底應該執行哪個函數實現。而採用訊息結構的語言,不論是否多態,總是在運行時才會去查所要執行的方法。實際上,編譯器甚至不關心接收訊息的對象是何種類型。接收訊息的對象問題也要在運行時處理,其過程叫做“動態綁定”。
2、Objective-C的執行階段程式庫
Objective-C的重要工作都由“執行階段程式庫”而非編譯器完成的。執行階段程式庫提供了Objective-C的物件導向的能力,使用Objective-C的物件導向特性所需的全部資料結構及函數都是由“執行階段程式庫”提供的。舉例來說,運行期組件中含有全部記憶體管理方法。
3、Objective-C的記憶體模型
Objective-C中的對象是以指標來指示的。想要聲明一個變數,令其指代某個對象,可用以下文法: NSString * someStr = @“The string”; 這種文法基本上是照搬C語言的,它聲明了一個名為someString的變數,其類型是NSString*。也就是說,此變數為指向NSString的指標。所有Objective-C語言的對象都必須這樣聲明,因為對象所佔記憶體總是分配在“堆空間”中,而絕不會分配在“棧”上。不能在棧中分配Objective-C對象。 如下代碼:
NSString * someStr = @“The string”;
NSString * anothreStr = someStr;
只有一個NSString執行個體,然而又兩個變數指向此執行個體。兩個變數都是NSString* 型,這說明當前“棧幀”裡分配了兩塊兒記憶體,每塊記憶體的大小都能容下一枚指標(在32位架構的電腦上是4位元組,64位電腦上是8位元組)。這兩塊兒記憶體裡的值都一樣,就是NSString執行個體的記憶體位址。
分配在堆中的記憶體必須直接管理,而分配在棧上用於儲存變數的記憶體則會在其棧幀彈出時自動清理。
Objective-C將對記憶體管理抽象了出來。不需要用malloc及free來分配或釋放記憶體。Objective-C運行時環境把這一部分工作抽象為一套記憶體管理架構,名叫“引用計數”。
如果只是儲存int、float、double、char等非物件類型,那麼通常使用CGRect這種結構體就可以了。CGRect是C語言結構體。儲存在棧上。整個系統架構都在使用這種結構體,因為如果改用Objective-C對象來做的話,效能會受到影響。與建立結構體相比,建立對象還需要額外開銷,例如分配及是否堆記憶體等。
第2條:在類的標頭檔中盡量少引用其他標頭檔 1. 將引入標頭檔的時機盡量延後,只在確有需要時才引入,這樣就可以減少類的使用者所需引入的標頭檔梳理。縮短編譯時間。
2. 向前聲明可以解決兩個類循環參考的問題,當兩個類循環參考的時候。使用#import而不是#include雖然不會導致死迴圈,但卻意味著兩個類裡有一個無法被正確編譯。
3. 有時候必須要在標頭檔中引用其他標頭檔。如果你寫的類繼承自某個超類,則必須引入定義那個超類的標頭檔。同理,如果要聲明你寫的類繼承自某個超類,那麼必須由完整的定義,且不能使用向前聲明。向前聲明只能告訴編譯器有某個協議,而此時編譯器卻要知道該協議中定義的方法。所以協議通常聲明在一個單獨的檔案中(代理協議除外,代理協議單獨聲明是沒有任何意義的)。
第3條:多用字面量文法,少用與之等價的方法
字面量文法實際上只是一種“糖衣文法”,其效果與常規文法等效但是開發人員用起來卻更加方便。“糖衣文法”可令程式更易讀,減少代碼出錯幾率。
要點:
1、應該使用字面量文法來建立字串、數值、數組、字典。與建立此類對象的常規方法相比,這麼做更加簡明扼要。 2、應該通過取下標操作來訪問數組下標或字典中的鍵所對應的元素。 3、用字面量文法建立數組或字典時,若值中有nil,則會拋出異常。因此,務必確保值裡不含nil。
字面量文法的局限性:
1、所建立出的對象必須屬於Foundation架構。要想建立自訂子類執行個體,必須採用“非字面量文法”。
2、字面量文法建立出來的字串、數組、字典對象都是不可變的。若想要可變版本的對象,則需賦值一份:
NSMutableArray * mutable = [ @[@1 , @2 , @3 , @4] mutableCopy ];
第4條:多用類型常量,少用#define預先處理指令
以如下預先處理為例為例:
#define ANIMATION_DURATION 0.3 1、預先處理指令不攜帶類型資訊,類型常量攜帶類型資訊。
2、預先處理指令範圍不明確,假設該指令聲明在某個標頭檔中,那麼所有引入了這個標頭檔的代碼,其 ANIMATION_DURATION 都會被替換 。 3、即使有人重新定義了常量值,編譯器也不會產生警告,這將導致應用程式中的常量值不一致。
在實現檔案中定義“只在編譯單元內可見的常量” static const NSTimeInterval kAnimationDuration = 0.3 ;
變數一定要同時用static和const來聲明。const確保變數不可變,static則意味著該變數僅在定義此變數的編譯單元中可見。假如聲明此變數時不加static,則編譯器會為它建立一個“外部符號”。此時若是另一個編譯單元中也聲明了同名變數,那麼編譯器會報錯。實際上,若干一個變數即聲明為static,又聲明為const,那麼編譯器根本不會建立符號,而是會像#define預先處理指令一樣,把所有遇到的變數都替換為常值。不過還是要記住:用這種方式的常量帶有類型資訊。 有時需要對外公開某常量。此類常量需放在“全域符號表”中,以便可以在定義該常量的編譯單元之外使用。應該這樣來定義
//In the header file
extern NSString * const EOCStringConstant;
//In the implementation file
NSString *const EOCStringConstant =@"VALUE";
這種常量要出現在全域符號表中,所以其名稱應加以區隔,通常用與之相關的類名做首碼。總之,勿使用預先處理指令定義常量,而應藉助編譯器來確保常量正確。
第5條:枚舉的使用 C++11標準修訂了枚舉的某些特性。其中一項改動是:可以指明用何種“底層資料類型”來儲存枚舉類型的變數。這樣做的好處是,可以向前聲明枚舉變數了。若不指定底層資料類型,則無法向前聲明枚舉類型。因為編譯器不清楚底層資料類型的大小,所以在用的此枚舉類型時,也就不知道究竟該給該變數分配多少記憶體空間。
如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那麼就將各選項值定義為2的冪,以便通過按位或操作將其組合起來。
用NS_ENUM與NS_OPTIONS宏來定義枚舉類型,並指明其底層資料類型。這樣做可以確保枚舉是用開發人員所選的底層資料類型實現出來的,而不會採用編譯器所選的類型。
在處理枚舉類型的switch語句中不要實現default分支。這樣的話,加入新枚舉之後,編譯器就會提示開發人員:switch語言並未處理所有枚舉