天天看點

Programming with Objective-C——翻譯2章

二章 類定義

1、Classes Are Blueprints for Objects類是對象的藍圖

一個類描述了相同類型對象的屬性和行為。對于字元串對象(OC裡是NSString類的實作),它的類中提供了檢驗和轉換字元串對象的多種方式。比如數值類(NSNumber)提供了與内部數值類型相關的一些功能,如将一種數值類型轉換成另外一種類型。

現實中不同建築可由同一套圖紙建造,它們差別僅在于建築内飾。類似地,同一個類的所有對象都具有相同的屬性和行為。所有的NSString類對象都有相同的行為,不管它們内部是什麼字元串。任何對象都有其特定用途。你知道一個字元串對象存放的是字元串,但你不必知道它如何去存儲串内字元。

你也不用知道這個對象内部是如何操縱字元的,你隻需了解如何使用這個對象。比如讓它輸出字元串,或要求它将内部所有字元轉換成大寫并存儲到一個新對象中。

OC類接口也指明了對象間的互動方式。即:接口定義了兩對象之間或是對象同外界之間的交流方式。

2、Mutability Determines Whether a Represented Value Can Be Changed類的易變性

有些類型對象是不可變的。就是說建立這類對象時候必須初始化,之後便不能被修改。OC中,NSString和NSNumber類對象都是不可變的。就是說你必須再次建立一個NSNumber對象來存放與之前不同的數值。但有些不變類也提供了可變版本。當你需要在運作時對一個字元串對象進行修改,如在字元串尾附加網絡上接收的内容,你可以使用NSMutableString對象來達到目的。NSMutableString類對象的行為同NSString類對象基本相同,不同之處在于前者内部字元串可在運作時修改。

NSString和NSMutableString有許多相同行為,盡管它們是兩個不同的類。比起完全從零開始實作兩個具有相似行為的類,更好的選擇是使用繼承。

3、Classes Inherit from Other Classes類的繼承

在自然界中,我們通過分類學的方法将動物進行區分,可以通過種、屬、族的不同來進行分類。這些分類是具有層級屬性的,比如許多不同的種可能會具有相同的屬,又比如不同的屬可以有相同的族。(生物學中的詞暫時亂的:譯注)

例如大猩猩、人類以及猩猩,顯然具有許多相同之處。(中文的猩猩和大猩猩貌似沒有進行區分:譯注)雖然三者是不同種的生物,也是不同的屬,不同的部,但是這三者都屬相同的族:人科(Hominidae)。如圖1-1所示:

Programming with Objective-C——翻譯2章

在面向對象程式設計的世界中,對象也是通過層次關系進行分類的。比起在生物學中用到許多不同名詞(屬、族、種。。。)來描述這種層次關系,程式設計中的對象組織隻是簡單地用到了:類。就像生物學中人類可以繼承某個人科成員一樣,OC中類也可以繼承。

當一個類繼承了另一個類後,子類便擁有了父類的所有行為和屬性。子類中還可定義自己獨有的行為和屬性,或是覆寫父類的某些行為。

在OC字元串類的例子裡,NSMutableString類繼承了NSString類,如圖1-2所示:

Programming with Objective-C——翻譯2章

NSString類方法在NSMutableString類中都可用,比如查詢特定字元串或請求新的大寫字元串對象。另外NSMutableString還加入了其它一些獨有的方法,如允許使用者附加、插入、替換、删除子串或特定字元。

4、The Root Class Provides Base Functionality根類(基類)

所有生物都具備“生”的特征,而OC中的對象也有一些共同的特征。

當兩個對象進行通信時,一方會希望另一方提供某些基本接口。是以,OC裡定義了一個基類NSObject(絕大多數類都繼承自它)。兩個對象互動時,至少用NSObject類裡面定義的接口進行互動。

當你自定義類的時候,你的類最小限度上應繼承自NSObject。總之,你應從Cocoa或Cocoa Touch中找最貼近需求的類,然後進行繼承。

比如你想在iOS應用中建立一個按鈕,但系統提供的UIButton不能完全滿足要求,那就應該建立一個繼承自UIButton的新類來達到要求,而不是繼承自NSObject的。如果這個新類繼承自NSObject,為滿足需要功能,你就要再次完成一遍UIButton已經做好的所有複雜工作。如果通過UIButton繼承,你的新類在以後将可以自動獲得任何UIButton類上的更新檔修複和性能提升。

UIButton類繼承自UIControl類,UIControl類描述了iOS上使用者界面控制行為的共同特征。UIControl類又繼承自UIView類,UIView類用于螢幕顯示。UIView類繼承自UIResponder類,這個類作用是對使用者的輸入(按壓,手勢,晃動)進行響應。最後,這顆繼承樹到了根節點NSObject,UIResponder類繼承自UIObject類,如圖1-3所示:

Programming with Objective-C——翻譯2章

上面的繼承連結清單明,任何UIButton的子類不僅繼承了它的功能,而且還繼承了UIButton所有祖先的功能。這個繼承鍊最後的結果就是,UIButton類按鈕能夠工作,也能在螢幕上顯示,也能響應使用者的輸入,并且可以同其它基本的Cocoa Touch類對象進行通信。

記住程式設計中你用到的類繼承鍊,這樣可以準确達到想要的功能。在Cocoa和Cocoa Touch庫的類引用文檔中可以輕松導航并且查詢到任何類及其祖先類。如果你在一個類的接口或引用中不能找到所需的功能,很有可能繼承鍊上它的祖先類具有你需要的功能。

5、The Interface for a Class Defines Expected Interactions定義類的接口進行通信

之前我們就了解,面向對象程式設計最大的優點:你不需要知道類對象的構造和内部運作,你隻需要知道如何同對象進行互動。(對象即黑盒:譯注)更準确地說,對象在設計時應該是隐藏了其内部實作細節的。

比如你在iOS應用中使用了一個UIButton對象,你不用知道它是如何操縱像素來顯示到螢幕上的。你隻需知道如何修改對象的屬性,比如标題和顔色。當這個對象被添加到界面,就肯定會正确地顯示,并且行為和你預想的一緻。

在自定義類時,首先應明确這個類的公共屬性和行為。比如什麼屬性是能被外界通路,是否允許這些屬性被修改,其它類型對象如何同這個對象互動。

這些資訊由接口接收,即定義了其它類對象同自定義類對象的互動途徑。自定義類的接口聲明和接口定義是分開的,後者構成了類的實作。在OC中,接口聲明和實作通常存放在不同檔案中,以便于隻将類接口公開。

6、Basic Syntax基本文法

OC中類接口聲明文法如下所示:

@interface SimpleClass : NSObject

@end

這個例子中聲明了SimpleClass類,它繼承自NSObject。

類的公共屬性和行為在@interface和@end之間定義。在這個例子中,子類沒有添加額外的元素,是以SimpleClass類的行為完全繼承自NSObject。

7、Properties Control Access to an Object‘s Values類對象的屬性控制

對象常擁有公共屬性。假如在程式中定義Person類,你可能會需要一個字元串類型的屬性用于描述姓名。

屬性聲明應該包含在類聲明内部,如下所示:

@interface Person : NSObject

@property NSString *firstName;

@property NSString *lastName;

@end

這個例子裡,Person類中聲明了兩個公共屬性,都是NSString類型。

兩個屬性都是OC對象,是以能加星号表示它們是對象指針。由于是聲明語句,和C中任何的變量聲明類似,是以末尾有分号。

你可以決定再加入一個屬性來描述一個人的出生日期,這樣除了按姓名之外,你還可以按年齡對這些人進行分組。可以使用一個數值對象:

@property NSNumber *yearOfBirth;

但使用數值對象存儲生日就太大材小用了。一個解決方案是使用C裡面提供的原子類型來存儲數值,比如一個int:

@property int yearOfBirth;

8、Property Attributes Indicate Data Accessibility and Storage Considerations屬性修飾

上面例子裡聲明的屬性具有完全公共通路權限的。也就是說其他對象可以對屬性進行自由讀寫。

在某些情況下,你可能并不願某些屬性被輕易修改。如同在現實世界中,一個人想改名的話,需要準備許多材料才行。如果你正在寫一個公民身份登記程式,可将屬性中的人名設定成隻讀,任何修改人名的請求都必須經過另一個中間對象,由這個中間對象來驗證請求,并決定是否允許修改。

OC屬性聲明中可以包含屬性修飾,用于指定一個屬性是否隻讀。在實際中,Person類的接口可以這樣聲明:

@interface Person : NSObject

@property (readonly) NSString *firstName;

@property (readonly) NSString *lastName;

@end

屬性修飾在@property關鍵字後面的括号内指定,詳見Declare Public Properties for Exposed Data。

9、Method Declarations Indicate the Messages an Object Can Receive對象方法聲明

上面的例子介紹的都是簡單對象模型,隻講到了資料的封裝。在Person類的例子裡,除了通路兩個屬性,可能并不需要其它功能。但是類的本質,卻是對類的任何屬性進行操作。

前面談到OC軟體都是以大量對象為基礎建立起來的,必須認識到對象之間存在消息傳遞。在OC中,對象間消息傳遞是通過方法調用實作的。

OC方法在概念上與C和其它程式設計語言中的函數類似,但在文法上并不相同。一個C函數聲明是這樣的:

void SomeFunction();

與之等價的OC方法聲明是這樣的:

- (void)someMethod;

上面的method沒有參數。關鍵字void被放在括号内,表示這個方法無傳回值。

傳回類型前的減号“-”表示這是一個對象方法,即它可以被這個類的對象調用。與之相對的是類方法,二者差別在于類方法可以被類本身調用,詳見Objective-C Classes Are also Objects。

和C中的函數原型聲明一樣,OC中方法聲明最後也要加上分号“;”。

10、Methods Can Take Parameters方法的參數

如果你需要聲明一個帶參方法,聲明的文法和C函數不一樣。

對于C函數,參數在括号内指定,如下所示:

void SomeFunction(SomeType value);

OC中将方法參數作為方法名的一部分,以冒号分隔,如下所示:

- (void)someMethodWithValue:(SomeType)value;

- (void)someMethodWithFirstValue:(SomeType)value1 secondValue:(AnotherType)value2;

someMethodWithFirstValue:secondValue:

-(void)someMethodWithFirstValue:(SomeType)info1 secondValue:(AnotherType)info2;

-(void)someMethodWithFirstValue:(SomeType)info1 anotherValue:(AnotherType)info2;

-(void)someMethodWithFirstValue:(SomeType)info1 secondValue:YetAnotherType)info2;

11、Class Names Must Be Unique類名必須唯一

@interface XYZPerson : NSObject

@property (readonly) NSString *firstName;

@property (readonly) NSString *lastName;

@end

12、The Implementation of a Class Provides Its Internal Behavior類的實作

12.1、Basic Syntax類實作中的基本文法

#import "XYZPerson.h"

@implementation XYZPerson

@end

12.2、Implementing Methods方法的實作

@interface XYZPerson : NSObject

- (void)sayHello;

@end

#import "XYZPerson.h"

@implementation XYZPerson

- (void)sayHello {

NSLog(@"Hello, World!");

}

@end

</pre><pre name="code" class="objc"><pre name="code" class="objc">- (void)sayhello {

}

- (void)sayHello{

NSLog(@"Hello, World!");

}

13、Objective-C Classes Are also Objects 類也是對象

+ (id)string; //構造空串

+ (id)stringWithString:(NSString *)aString; //構造一個存放aString的串

+ (id)stringWithFormat:(NSString *)format, …; //構造一個指定格式字元串

+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error; //存放檔案内字元串?

+ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc; //存放C串?

14、Exercises練習:

(NS代表的是NeXTSTEP,是Jobs在1985年離開水果的時候建立的公司名稱。)

當編寫OSX或iOS程式時,你多數情況都是在和對象打交道。OC中的對象和其它面向對象語言中的一樣:對象是資料和相關操作的封裝。

一個程式由許多互相關聯的對象組成,它們之間進行通信,共同解決一個特定問題。比如顯示一個虛拟接口,響應使用者輸入,存儲資訊等。對于OSX和iOS開發,你不需要從零開始建立各種對象,因為已經有一個龐大的對象庫可供使用,這個庫由Cocoa(OSX平台)或Cocoa Touch(iOS平台)架構提供。

有些對象可直接使用,如字元串或數字等基本資料類型,以及按鈕和表格等使用者界面元素。另外一些則由你在其中編寫代碼,以實作需要的功能。開發中你應保證自定義對象最優化,決定架構對象和自定義對象如何組織,進而讓你的程式具有獨特的界面和功能。

在面向對象程式設計中,對象是類的執行個體化産物。本章中自定義了一個單接口類XYZPerson,外界通過接口來使用類及其對象。接口包含類能夠接收的消息清單,是以你還要提供類的實作,即消息處理代碼。

和傳回類型一樣,參數類型在括号内指定,後面跟上參數名,類似于C中的變量類型轉換。

如果你需要多個參數,那麼文法和C中的更不一樣了。C裡面的多個參數是在括号内指定,并且由逗号分隔;而在OC中,多個參數的文法如下所示:

在上面的代碼中,value1和value2是兩個形參名,當方法被調用時,這兩個參數接收傳入的實參。

有的程式設計語言中允許使用命名參數,但在OC中沒有。調用方法的格式必須和方法聲明中的形式一緻,實際上,上面代碼中secondValue也是方法名的一部分。

這樣提升了OC代碼的可讀性,詳見You Can Pass Object for Method Parameters。

Note: The value1 and value2 value names used above aren’t strictly part of the method declaration, which means it’s not necessary to use exactly the same value names in the declaration as you do in the implementation. The only requirement is that the signature matches, which means you must keep the name of the method as well as the parameter and return types exactly the same.

注意:上面代碼中value1和value2這兩個名字并非方法聲明的一部分,也就是說你在方法實作中并不一定要使用和方法聲明中一緻的參數名。唯一的要求是保證類型比對,即你必須保證方法名和參數類型在聲明和實作中是一緻的。

來看一個例子,下面的代碼與之前代碼是完全相同的。

這兩行代碼聲明的則和之前的代碼中方法不同:

應用内定義的類名稱必須唯一,如果你嘗試使用一個已存在的類名來建立新類,編譯時會出錯。

為避免這樣的錯誤,可以添加類名字首(三個或以上字母)。添加的字首可以是和你應用相關的,也可以是随意的。之後的所有例子裡都使用了類名字首,如下所示:

曆史注記:關于為何許多類前面都帶有NS字首,這是因為Cocoa和Cocoa Touch的曆史所緻。Cocoa架構最開始用于NeXTStep(NS字首)系統應用開發。當1996年蘋果收購NeXTStep後,NeXTStep系統大部分都被整合進了OSX系統中,包括Cocoa架構和已經存在的類名。而Cocoa Touch架構是為iOS系統使用的,和Cocoa架構類似。一些類在Cocoa和Cocoa Touch中都可用,而另外大部分的類則隻适用于自己的平台。

兩字母字首如NS和UI(iOS中用于使用者界面元素)被蘋果保留。

相比類名,方法名和屬性名隻需保證在類中唯一既可。雖然C程式中每個函數都必須具有唯一的名字,但OC中卻不是這樣。OC中必須接受,或者說時常需要在不同的類中建立同名方法。在同一個類聲明中,方法名是唯一的,也就是特定方法隻能定義一次。但如果你想在子類中覆寫一個父類方法,就必須在子類中使用和父類相同的方法名。

和方法名一樣,對象屬性和執行個體變量(參見Most Properties Are Backed by Instance Variables)隻需在被定義的類中保證唯一。如果是全局變量,則必須保證在一個應用或工程中名字唯一。

其它的命名規範和建議,請參見Conventions。

當你使用@interface關鍵字聲明一個類 (包括屬性和公共方法) 之後,就需要寫這些方法的實作。

之前就談到過,類的聲明通常被放在頭檔案中,擴充名為.h。而類的實作通常被放在源檔案中,擴充名為.m。

當在頭檔案中聲明一個類後,你還需要告訴編譯器從哪個源檔案找到類的實作。OC中提供#import預處理指令來完成這項工作。這條指令可以保證隻包含一次頭檔案,其餘則和C語言中#include指令類似。

需要注意,在預處理指令末尾不能添加分号。

類實作的基本文法如下所示:

在類中聲明的所有方法都需要在這個檔案(.m)中實作。

下面示範一個XYZPerson類,它裡面包含一個方法sayHello。

這個類的實作如下所示:

這個例子中NSLog()函數的作用是向終端輸出資訊。NSLog()函數和C中printf()函數類似,它接受可變數量參數,并且第一個參數必須是OC字元串(以@開頭)。

方法的實作寫在大括号内({}),和C語言中函數的定義類似。在方法的實作代碼中,方法名、參數類型以及傳回值類型必須和聲明中的完全一緻。

OC和C一樣是大小寫敏感的,請看如下代碼:

這裡的sayhello方法和之前的sayHello方法是完全不同的。

OC中方法名應以小寫字母開頭,命名原則是:讓名稱盡可能詳細地描述這個方法功能,這與C中函數命名原則不同。當一個方法名中包含多個單詞,則可以使用camel case(中間單詞的首字母大寫,和駱駝駝峰一樣起伏,故camel case)來讓代碼更加易讀。

注意靈活使用OC中的代碼空白 (增強代碼可讀性)。你可以選擇空格或Tab進行代碼縮進,有時還會看到位于空行上的左大括号,如下所示:

如果使用Xcode進行程式設計,它能按照使用者設定自動完成代碼縮進。詳見Changing the Indent and Tab Width in Xcode Workspace Guide。

在下一章Working with Objects中,你可以找到更多的對象方法實作的例子。

在OC中,類也是一種叫Class的不透明類型對象(即類也有類型,類也是對象)。類沒有屬性,但它可以接收消息(定義類方法和對象方法有差別)。

類方法的典型應用是factory method(暫譯作構造方法),構造方法可以完成對象的空間配置設定和初始化工作。詳見:Objects Are Created Dynamically。(沒有對象之前怎麼調用方法,需要用到類方法,使用類方法建立對象,再由對象調用對象方法工作:譯注)以NSString類為例,它裡面有許多的構造方法,有的方法可以建立空字元串對象,有的可以建立包含指定字元串的對象。如下所示:

從上面的例子可以看到,類方法前以加号(+)開始,而對象方法則以減号(-)開頭。

類方法的原型聲明和對象方法的一樣,寫在類的聲明中。類方法的實作也和對象的一樣,寫在@implementation塊中。

使用Xcode——建立工程——Command Line Tool模闆來建立一個OSX工程。送出後,指定工程類别為Foundation。

1.在Xcode中點選建立檔案,建立.m和.h檔案,并建立XYZPerson類,它繼承自NSObject類。

2.在XYZPerson類中加入first name、last name及date of birth屬性。

3.聲明對象方法sayHello,并對其進行實作。

4.聲明一個類的構造方法person。(在學完下一章之前不用實作它。)注意:完成練習後編譯代碼,你會得到”Incomplete implementation”的提示,這是因為構造方法person還沒有進行實作。

繼續閱讀