轉載請標明出處:http://blog.csdn.net/zhangxingping
類的實現 類的定義在結構上和類的聲明很相似。類的定義以@implementation命令字開始,以@end命令字結束:
@implementation 類名稱:超類名稱 { 執行個體變數聲明 } 方法實現 @end
需要說明的一點是,類的實現檔案中必須引入對應的介面檔案。例如,在Rectangle.m檔案中就必須引入Rectangle.h。另外,由於類的實現中沒有必要再次重複其引入檔案中的內容,因此這些類容可以被省略掉:
● 其超類的名稱
● 類中執行個體變數的聲明
這樣一來,引入其對應的介面檔案簡化了實現檔案,並使得在實現檔案中我們更能聚焦於類中各個方法的實現:
#import "類名稱.h" @implementation 類名稱 方法的實現 @end
其中方法的實現體和C語言中是一樣的,用一對大括弧括起來。在大括弧之前內容是和介面檔案中方法的聲明一樣,只是少了聲明時最後面的分號。例如:
+(id)alloc { ... } -(BOOL)isFilled { ... } -(void)setFilled:(BOOL)flag { ... }
使用變參的方法對參數的處理和普通的方法一樣:
#import <stdarg.h> ... - getGroup:group,... { va_list_ap; va_start(ap,group); ... }
引用執行個體變數 預設情況下,執行個體方法中是可以訪問類中所有的執行個體變數的。使用時直接使用其名稱就可以了。儘管編譯器會建立一個對等的C結構體來儲存這些執行個體變數,但是這個結構體對外是不可見的。我們不需要使用結構體運算子(.或者是->)來操作其中的資料。例如:下面方法的定義中使用到了接收者的filled變數:
-(void)setFilled:(BOOL)flag { filled = flag; }
儘管其中的filled變數以及訊息的接收者都不是作為參數傳入到方法中的,但是在類的方法實現中仍然是可以訪問的。這種文法大大簡化了Objective-C代碼的編寫。
當一個對象的執行個體變數不是訊息的接收者的時候,執行個體變數的類型必須被通過靜態類型的方式顯示地告知編譯器。在引用這些靜態類型的執行個體變數的時候,我們要使用結構體的指標運算子。
例如,Sibling類中聲明了靜態類型的twin作為其中的一個執行個體變數:
@interface Sibling: NSObject { Sibling *twin; int gender; struct features *appearance; }
只要靜態類型對象的執行個體變數的範圍包含了這個類(正如上面的程式段中那樣,由於twin執行個體變數的類型和類是一樣的),Sibling類的方法就可以直接設定其值:
- makeIdenticalTwin { if (!twin) { twin = [[Sibling alloc] init]; twin->gender = gender; twin->appearance =appearance; } }
執行個體變數的範圍 儘管執行個體變數是在介面檔案中進行聲明的,但是執行個體變數表達的則更多的是類的實現方式而不是類的使用方法。對象對外界提供的介面是方法而不是其自身內部的資料。
通常每一個執行個體變數都有一個與之一一對應的方法。如下:
-(BOOL) isFilled { return filled; }
但是這樣做不是必須的。某些方法返回的可能不是儲存在執行個體變數中的資訊;並且有些執行個體變數所儲存的資訊是不希望能被外界能感知的。
在類的不斷完善的過程中,儘管其方法可能保持不變,但是執行個體變數則有更大的可能性發生變化。但是只要類的執行個體間互動的訊息沒有發生變化,則這種內部執行個體變數的變化並不會影響到類對外界提供的介面。
為了強迫對象對外界隱藏其資料,編譯器對執行個體變數進行了限制,也就是說限制了這些執行個體變數在程式中的可見範圍。但是出於靈活性的考慮,允許程式員顯示地對這種限制範圍做出下面四種選擇之一:
┌───────────┬───────────────────────────────────────────────────────────────────────────────┐
│ 命令字 │ 含義 │
├───────────┼───────────────────────────────────────────────────────────────────────────────┤
│ @private │執行個體變數只能在聲明他的類中被訪問 │
├───────────┼───────────────────────────────────────────────────────────────────────────────┤
│ @protected│執行個體變數只能在聲明他的類及其該類的衍生類別中被訪問 │
├───────────┼───────────────────────────────────────────────────────────────────────────────┤
│ @public │執行個體變數在任何地方都可以被訪問 │
├───────────┼───────────────────────────────────────────────────────────────────────────────┤
│ │現代運行時系統中,@package類型的執行個體變數對於實現其類的可執行檔“影像”來說是可見 │
│ │的;而對於其他代碼來講都是@private的。 │
│ │Objective-C中的@package變數的範圍類似於C中的private_extern變數的範圍。任何 │
│ @package │位於該類的實現“影像”之外的地方對其的訪問都會導致連結錯誤。 │
│ │這種範圍的限制對於framework架構類來說是非常有用的。此時@private的限制太嚴格了 │
│ │而@protected或者是@public的限制又太寬鬆了。 │
└───────────┴───────────────────────────────────────────────────────────────────────────────┘ 譯者注:關於@package含義的描述中用到了“影像”一詞,原文中的詞為“image”.鑒於還沒有找到沒有合適的中文詞彙來對其進行描述,這裡說說譯者對其的理解:這裡的image通常指的是framework架構,動態庫或者可執行檔,而與“映像”一詞毫無關係。它指的是連結器在進行連結時能載入的“影像”。一個常見的執行階段錯誤就是:“dyld image not found",這裡的image就是這個意思。更多資訊可以參考: http://stackoverflow.com/questions/772600/what-does-the-package-directive-do-in-objective-c,不過是英文的喲。
┌────────────────────┐ ─┐ ─┐ ─┐
│ │ │ │ │
│ 聲明執行個體變數的類 │ ├───>@private │ │
│ │ │ │ │
└─────────┬──────────┘ ─┘ │ │
│ ├───>@protected │
┌─────────┴───────────┐ │ │
│ │ │ │
│ 繼承執行個體變數的類 │ │ ├──>@public
│ │ │ │
└─────────────────────┘ ─┘ │
│
│
┌─────────────────────┐ │
│ │ │
│ 其他的無關的代碼 │ │
│ │ │
└─────────────────────┘ ─┘
圖2-1 執行個體變數的範圍(不包括@package類型)
範圍命令字對跟隨其後的變數都起作用,直到遇到下一個命令字或者是列表結束。在下面的樣本程式中age和evaluation執行個體變數就是私人的(private);name,job和wage是保護的(protected);boss則是公有的(public):
@interface Worker : NSObject { char * name; @private: int age; char * evaluation; @protected: id job; float wage; @public: id boss; }
預設情況下,所以沒有顯示範圍命令字的變數都是@proetected。
類中聲明的所有變數,不管其前面的命令字是什麼,在該類範圍內都是可見的。例如,定義了job執行個體變數的類,如上面的Worker類,是可以在其方法中使用job這個變數的:
- promote:newPosition { id old = jog; job = newPosition; return old; }
很顯然,如果類不能訪問他的執行個體變數,那麼這些執行個體變數將變得毫無用處。
一般情況下,類還可以訪問其繼承而來的執行個體變數。類能夠在其資料結構的作用範圍內訪問之,這樣做是有意義的,特別是當我們把一個類看作是對其基類的更精細化的描述的時候。之前的promoteTo:方法是可以定義在任何從Worker類中繼承了job執行個體變數的衍生類別中的。
然而,對這種繼承做出限制以避免對這些執行個體變數的直接存取也是有必要的:
● 一旦衍生類別中訪問了繼承而來的執行個體變數,那麼聲明這些執行個體變數的類就被綁定到了這部分實現上。在以後的版本中,如果沒有經過仔細考慮對衍生類別的影響,就不能刪除這些變數或者是修改這些執行個體變數所扮演的角色。
● 更有甚者,如果衍生類別中訪問了或者是修改了繼承而來的執行個體變數的值,就有可能為聲明這些執行個體變數的類中引入bug,特別是當這些執行個體變數只是和聲明他們的類內部密切相關的時候。
為了達到把執行個體變數的範圍僅限於聲明他的類中 ,我們必須使用@private命令字。被@private命令字限定的執行個體變數只能被衍生類別通過其所在類提供的公有介面來訪問。如果其所在的類沒有提供對應的公有介面,則這些執行個體變數對與衍生類別來說是不可見的。
另外的一個極端就是@public命令字。他使得執行個體變數成為通用的變數,即使是在聲明他的類的範圍之外,或者是其衍生類別的範圍之外。一般情況下,別的對象必須通過發送訊息的方式來擷取儲存在一個執行個體變數中的資訊。然而,公有的執行個體變數卻是可以像C語言的結構體中的欄位一樣在程式的任何地方都可以被直接存取。
Worker *ceo = [[Worker alloc] init]; ceo->boss = nil;
此時對象必須是靜態類型的。
@public命令字破壞了對象隱藏其資料的能力。這種做法與物件導向的編程思想--資料是被封裝在對象中的,以避免由於粗心大意而引發的錯誤--背道而馳。因此共有的執行個體變數在非特殊情況下是應該極力避免的。