定義類 - Defining Classes

2018-08-12 21:19 更新

定義類 - Defining Classes

當你為 OS X 或 iOS 系統(tǒng)編寫軟件時,你的大部分時間都花在了對象上。Objective-C 中的對象就像其他面向?qū)ο缶幊陶Z言中的對象一樣:它們把數(shù)據(jù)與相關(guān)的行為打包。

一個應(yīng)用程序構(gòu)成了一個巨大的聯(lián)通對象的生態(tài)系統(tǒng),他們之間相互通信以解決具體問題,如顯示一個可視化界面、響應(yīng)用戶的輸入、或存儲信息。 OS X 或 iOS 開發(fā)中,您不需要從頭開始創(chuàng)建對象來解決每個問題;Cocoa (for OS X) 和 Cocoa Touch (for iOS) 為你提供一個包含可供你使用的現(xiàn)有對象庫。

其中一些對象是立即可用的,比如字符串和數(shù)字等基本數(shù)據(jù)類型,或像按鈕和表視圖這樣的用戶界面元素。一些是專為你以你需要的方式定制代碼。軟件開發(fā)過程涉及決定如何最好地定制和合并底層框架提供的對象和自己的對象,讓你的應(yīng)用具有其獨特的特性和功能。

在面向?qū)ο蟮木幊绦g(shù)語中,對象類的實例。本章示范了如何在 Objective–C 中通過聲明一個用來描述你打算使用的類及其實例的接口來定義類。這個接口包含此類可以接收到的消息列表,所以你還需要提供類的實現(xiàn),其中包含要執(zhí)行的代碼以響應(yīng)每個消息。

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

類描述了特定類型的對象的常見行為和屬性。字符串對象(在 Objective-C 中,這是類 NSString 的一個實例),該類提供了各種方法來檢查和轉(zhuǎn)換內(nèi)部的字符。同樣,此類過去常用于描述一個數(shù)字對象( NSNumber )提供圍繞內(nèi)部的數(shù)值,如將該值轉(zhuǎn)換為不同數(shù)字類型的功能。

同樣的,多個由相同的藍圖構(gòu)成的建筑在結(jié)構(gòu)上是相同的,類的每個實例共享相同的屬性和行為來作為該類的所有其他實例。每個 NSString 實例也一樣,無論它的內(nèi)部字符串如何。

任何特定的對象是用于以特定的方式而設(shè)計的。你可能知道一個字符串對象表示某些字符的字符串,但你不需要知道確切的內(nèi)部機制,用來存儲這些字符。你不懂的內(nèi)部行為對象本身用于直接處理的特點,但你要知道如何你預(yù)期的交互作用與對象,也許是為了索取特定字符或請求一個新的對象,在其中所有原始字符轉(zhuǎn)換為大寫。

在 Objective-C 中,類接口指定一個給定的類型的對象是如何被其他對象使用的。換句話說,它定義了類的實例與外部世界之間的公共接口。

可變性決定了一個代表值能否被取代 - Mutability Determines Whether a Represented Value Can Be Changed

一些類定義對象是不可變的。這意味著當一個對象創(chuàng)建,并且隨后不能由其他對象更改時,必須設(shè)置內(nèi)部內(nèi)容。在 Objective-C 中,所有基本的 NSString 和 NSNumber 對象都是不可變的。如果您需要代表一個不同的數(shù)字,則必須使用一個新的 NSNumber 實例。

不可變的類還提供了一個可變的版本。如果您一定需要更改在運行時的字符串內(nèi)容,例如他們在收到通過網(wǎng)絡(luò)連接時填加字符,您可以使用 NSMutableString 類的一個例子。這個例子像 NSString 對象一樣,除此之外他們還提供更改此對象表示的字符。

雖然 NSString 和 NSMutableString 是不同類,但他們有許多相似之處。你并不需要從零開始的編寫兩個獨立類,只是有時候碰巧有一些類似的行為要寫兩個完全獨立的類,它完全可以利用現(xiàn)有的東西完成。

類的繼承 - Classes Inherit from Other Classes

在自然的世界里,將動物分為像種、 屬、族這樣的各個門類。這些群體是分層的,許多的物種可能都屬于同一屬,許多的屬可能同是一個族。

舉個例子,猩猩、狒狒和人類有明顯的相似性。雖然他們都屬于不同的物種,和甚至不同的屬,部落和亞科,但是它們分類學相關(guān),因為它們都屬于同一個族 (稱為"人科"),如圖 1-1 所示。

圖 1-1 物種之間的分類關(guān)系

物種之間的分類關(guān)系

在全世界的面向?qū)ο蟮木幊讨?,對象則是也分成為各層次的組。對象被簡單的分成許多類,并不會為了像 genus、species 這樣不同的層次使用不一樣的術(shù)語。同樣的,人類作為人科家族成員繼承著某些特征,那么就可以在從父母類功能性的傳承中建立某一類。

當一個類從另一個類有所繼承時,新的類就會繼承舊類的全部行為和屬性。它也有機會來定義它自己的其他的行為和屬性,或也可以重寫繼承來的行為屬性。

在 Objective-C 字符串類的情況下, NSMutableString 描述指定的類繼承于 NSString,如圖 1-2 所示。所有由 NSString 提供的功能都是可用于 NSMutableString 的,如查詢特定的字符或要求更改新的大寫字符串,但 NSMutableString 添加了可以讓您附加、插入、替換或刪除子字符串和單個字符的方式。

圖 1-2 NSMutableString 的繼承

NSMutableString 的繼承

The Root Class提供基本的功能 - The Root Class Provides Base Functionality

所有生物體都擁有著一些基本的 "life" 特點,有些功能對于 Objective-C 中的對象是很常見的。

當 Objective-C 對象需要使用另一個類的一個實例時,它需要其他類提供一定的基本特征和行為。為此,Objective-C 定義根類從中絕大多數(shù)的其他類繼承,稱為 NSObject 。當一個對象遇到另一個對象時,它將至少能夠使用 NSObject 類描述所定義的基本行為進行交互。

當您定義您自己的類時,你應(yīng)該至少從 NSObject 中繼承。一般情況下,你應(yīng)該找一個提供你所需最接近的功能的 Cocoa or Cocoa Touch 對象,然后從其中繼承。

如果您想要在 iOS 應(yīng)用程序中使用自定義的按鈕,提供的 UIButton 類不能提供足夠可自定義的屬性以滿足您的需要,從 NSObject 中創(chuàng)建一個新類比從 UIButton 中創(chuàng)建更有意義。如果你只是從 NSObject 簡單的繼承,你將需要由 UIButton 類定義的所有復(fù)雜的視覺交互和通信,這只是為了讓你的行為方式按照用戶的期望的按鈕定義來實現(xiàn)。此外,通過從 UIButton 繼承,你的子類會自動地獲得任何未來的功能增強或可能應(yīng)用于內(nèi)部 UIButton 行為的 bug 修復(fù)。

UIButton 類本身定義繼承 UIControl ,描述了在 iOS 上所有用戶界面控件的常見基本行為。反過來, UIControl 類繼承 UIView ,給在屏幕顯示的對象提供常用功能。UIView 繼承于 UIResponder,允許它響應(yīng)用戶輸入,如水龍頭、 手勢。最后,也是最重要的,UIResponder 繼承 NSObject,圖 1-3 所示。

圖 1-3 UIButton 類的繼承

UIButton 類的繼承

這一連串的繼承是指 UIButton 任何自定義子類將不僅是繼承聲明了 UIButton 本身的功能,也依次從每個超類繼承功能。你會最終以一個像按鈕的對象結(jié)束,它可以在屏幕上自我顯示、 對用戶輸入作出響應(yīng)以及與所有基本的 Cocoa Touch 對象進行通信。

對于你需要使用的類,要把它的繼承鏈牢記于心,以便它可以把它所有能做的做好。類參考文檔提供的 Cocoa and Cocoa Touch 允許從任何類可以輕松導航到每個超類。如果你在一個類接口或引用中找不到你需要的,它可能很好的在進一步的超類中定義了。

類接口定義預(yù)期交互 - The Interface for a Class Defines Expected Interactions

面向?qū)ο缶幊痰闹T多好處之一就是前面提到的想法 — —要使用一個類,那么你所需要知道的就是如何與它的實例進行交互。更具體地說,應(yīng)該設(shè)計一個讓對象隱藏其內(nèi)部實現(xiàn)的細節(jié)。

如果你在 iOS 應(yīng)用程序中使用標準的 UIButton ,你不需要擔心像素在屏幕上如何操縱按鈕來顯示。你需要知道的就是您可以更改某些屬性,比如按鈕的標題和顏色,并當你將它添加到您可視化界面時表示信任,它將會以你期望的方式正確顯示。

當您定義您自己的類時,您需要先搞清楚這些類的公共屬性和行為。你想訪問哪些公開屬性?你應(yīng)該讓這些屬性改變嗎?其他對象與您的類的實例如何溝通?

此信息進入您的類的接口 — — 它定義了你打算與你的類的實例中的其他對象進行交互的方式。公共界面由您的類的內(nèi)部行為進行獨立描述,它組成了類。在 Objective-C 中,接口和安裝通常放置在獨立的文件中,這樣你只需要使接口開放。

基本語法 - Basic Syntax

在 Objective-C 中描述一個類接口的語法如下所示:

@interface SimpleClass : NSObject

@end

本例聲明了一個從 NSObject 中 繼承來的名為 SimpleClass 的類。

@interface 聲明內(nèi)部定義的公共屬性和行為。在此示例中,沒有指定屬性或行為超出超類,所以唯一的可在 SimpleClass 上實現(xiàn)的實例預(yù)計功能是從 NSObject 繼承來的。

屬性控制對對象值的訪問

對象通常具有屬性供公眾查閱。例如,如果您在一款記錄保存軟件中定義了一個類來表示人,那你可能需要一個決定字符串來表示一個人的姓和名的屬性。

這些屬性的聲明應(yīng)添加內(nèi)部接口,像這樣:

@interface Person : NSObject

@property NSString *firstName;
@property NSString *lastName;

@end

在此示例中,Person 類聲明了兩個公共屬性,這兩個屬性是 NSString 類的實例。

這兩個屬性都是對 Objective-C 的對象而言的,所以他們使用星號以表明它們是 C 指針。他們也像在 C 語言中其他變量的聲明語句一樣,需要以分號結(jié)尾。

您可能會決定添加一個屬性來表示一個人出生日期,以便可以按年齡給人分組,而不是只按名稱進行排序。你可以對一個數(shù)字對象使用這樣的屬性:

@property NSNumber *yearOfBirth;

但這只是為了存儲一個簡單的數(shù)字值,可能算是矯枉過正。那么可以使用另一種由 C 語言提供替代方法,持一個整數(shù)標量值:

@property int yearOfBirth;

屬性的特性表明數(shù)據(jù)可存取性和存儲的注意事項 - Property Attributes Indicate Data Accessibility and Storage Considerations

這個示例展示了到目前為止所有為了完善公共訪問所聲明的屬性。這意味著其他對象可以同時讀取和更改屬性的值。

在某些情況下,您可能希望聲明一個屬性不能被改變。在現(xiàn)實世界中,一個人必須填寫大量的文書工作來更改其記錄的第一個或最后一個名稱。如果你正在寫官方記錄的應(yīng)用程序,您可以選擇指定一個人名字的公共屬性為只讀,任何更改都需要通過中介對象來負責驗證請求并選擇通過或拒絕它。

Objective-C 屬性聲明可以包含屬性的特性,用于指示是否一個屬性應(yīng)設(shè)置為只讀模式。在官方記錄應(yīng)用程序中,人名的類接口可能如下所示:

@interface Person : NSObject
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
@end

屬性的特性在括號內(nèi)的 @property 關(guān)鍵字后指定,完整地在聲明的公共屬性公開的數(shù)據(jù)中描述。

方法聲明對象可接收消息 - Method Declarations Indicate the Messages an Object Can Receive

到目前為止的例子涉及到描述一個典型的模型對象或主要用于封裝數(shù)據(jù)的對象的類。在 Person 類中,很可能有不需要能夠訪問這兩個聲明屬性之外的任何功能。然而,大部分的類包括除了定義屬性以外的行為。

既然 Objective-C 軟件是由對象的大型網(wǎng)絡(luò)所組成,標記那些能夠通過發(fā)送信息相互作用的對象就非常重要了。在 Objective-C 術(shù)語中,一個對象發(fā)送消息到另一個對象是通過對該對象調(diào)用的方法實現(xiàn)的。

盡管語法有很大不同, Objective-C 在概念上還是類似于 C 和其他編程語言中的標準函數(shù)的。C 函數(shù)聲明如下所示:

void SomeFunction();

與之相同的用 Objective-C 來定義如下所示:

- (void)someMethod;

在這種情況下,該方法不具有參數(shù)。Void 在聲明之初寫在小括號中來表示這個方式是不會再程序結(jié)束時返回任何值的。

在方法名稱前面的減號 (-) 表示它是實例方法,可以在類的任何實例調(diào)用。這將它從類的方式中區(qū)別出來,可以調(diào)用類本身,Objective-C Classes Are also Objects. 中有所敘述。

正如 C 函數(shù)的原型,一個在 Objective-C 類的接口聲明就像其他的 C 語句一樣,需要分號終止。

Methods 可以使用參數(shù) - Methods Can Take Parameters

如果您需要聲明一個方法來帶一個或多個參數(shù),語法則是與一個典型的 C 函數(shù)非常不同的。 對于 C 函數(shù)的參數(shù)被指定在括號里,像這樣:

void SomeFunction(SomeType value);

Objective-C 方式聲明包括參數(shù)的名字,這里用到冒號,像這樣:

- (void)someMethodWithValue:(SomeType)value;

正如返回類型,參數(shù)的類型被指定在括號內(nèi),就像標準的 C 類型轉(zhuǎn)換一樣。 如果您需要提供多個參數(shù),語法又迥異于 C。將這些 C 函數(shù)參數(shù)依舊是指定在括號內(nèi),用逗號分開來彼此分開 ;在 Objective-C 中,方式聲明像這樣采取了兩種參數(shù):

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

在此示例中, value1 和 value2 是用在當方式被調(diào)用時實現(xiàn)訪問值時的名字,它們就像變量。

一些編程語言允許函數(shù)用所謂的命名參數(shù)來定義 ;要特別注意這不是 Objective-C。 方式中的變量被調(diào)用的順序必須與方式的聲明相匹配,事實上 secondValue : 方法聲明的一部分是方法的名稱:

someMethodWithFirstValue:secondValue:

這是一個有助于使 Objective-C 更加可讀的語言,因為通過方法調(diào)用傳遞的值是指定的內(nèi)聯(lián)函數(shù) ,旁邊的方法名稱的相關(guān)部分在You Can Pass Objects for Method Parameters 中有所描述。

注: 上面使用的 value1 和 value2 值的名稱不是嚴格意義的方法聲明,這意味著它是不需要像你在執(zhí)行中要使用完全相同值作為名稱一部分。它唯一的要求是簽名要相匹配,這意味著您必須保留該方法的參數(shù)名稱,并保證返回類型完全相同。 下面的例子,這種方法具有相同的簽名,就如上面所示:

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

類的名稱一定要與眾不同 - Class Names Must Be Unique

請?zhí)貏e注意在同一個應(yīng)用程序中每個類的名稱必須是唯一的,甚至跨越包括的庫或框架也要這樣。如果你試圖用一個項目中的現(xiàn)有類相同的名稱創(chuàng)建一個新類,您會收到一個編譯器錯誤。

出于這個原因,也建議您使用三個或更多字母定義任何類的名稱的前綴。這些字母可能涉及到您目前正在編寫的應(yīng)用程序,或者是可重復(fù)利用的代碼框架名字。 在本文檔的其余部分給出的所有例子都使用類名前綴,像這樣:

@interface XYZPerson : NSObject
@property (readonly) NSString *firstName;
@property (readonly) NSString *lastName;
@end

歷史注釋: 如果你想知道為什么所以你遇到的許多類有一個 NS 前綴,其實因為它是過去的 Cocoa and Cocoa Touch 歷史。Cocoa 開始生命的收集的框架,用于構(gòu)建 NeXTStep 操作系統(tǒng)的應(yīng)用程序。當蘋果公司在 1996 年買下一回時下, 一步步驟大量被納入OS X 上,包括現(xiàn)有的類名。介紹了可可觸摸 iOS 等同于可可豆 ;一些類,可在可可粉和可可觸摸,雖然也有大量的類獨有的每個平臺。

兩個字母前綴像 NS 和 UI (對于在 iOS 用戶的界面元素) 被蘋果公司所使用。 與此相反的是,方法和屬性的名稱,只需在定義它們的類內(nèi)唯一。雖然每個應(yīng)用程序中的 C 函數(shù)必須具有唯一的名稱,但在許多Object-C 中用相同名字定義類是完全可以接受的 (甚至是這樣更好) 。然而,你不能在相同的類聲明中不止一次的定義同一個方法,雖然你想要重寫從父類繼承的方法,但您必須使用原始聲明中使用的確切名稱。

作為與方法,對象的屬性和實例變量 (多數(shù)屬性受配于實例變量有所描述) 需要在他們定義的類中是唯一的。但是,如果要使用的全局變量,這些必須在應(yīng)用程序或項目內(nèi)唯一名稱。 更多的命名規(guī)定和建議詳見 Conventions。

類的實現(xiàn)規(guī)定其內(nèi)部的行為 - The Implementation of a Class Provides Its Internal Behavior

一旦你定義了一個類,其中包括的屬性和方法都是供公眾查閱的,您需要編寫代碼來實現(xiàn)的類行為。

如前所述,該接口的類通常放置在一個專用的文件中,通常被稱為一個頭文件,它一般都有文件擴展名 .h。寫 Objective-C 類里面一個源代碼文件以擴展名執(zhí)行 . m。

每當在頭文件中定義的接口時,你需要告訴編譯器在試圖編譯的源代碼文件中執(zhí)行前請先閱讀。為此,Objective-C 提供預(yù)處理器指令,#import。它類似于 C #include 指令,但可以確保文件是只包括在編譯過程中一次。 請注意預(yù)處理器指令有別于傳統(tǒng)的 C 語句,不使用分號終止。

基本語法 - Basic Syntax

為一個類提供實現(xiàn)的基本語法如下所示:

    #import "XYZPerson.h"

    @implementation XYZPerson

    @end

如果你聲明一個類接口中的方式,您需要在這個文件內(nèi)執(zhí)行他們。

執(zhí)行方法 - Implementing Methods

用這種方法,像這樣的簡單的類接口如下:

@interface XYZPerson : NSObject
- (void)sayHello;
@end

實現(xiàn)過程就像這樣:

    #import "XYZPerson.h"

    @implementation XYZPerson
    - (void)sayHello {
      NSLog(@"Hello, World!");
    }
    @end

本示例使用 NSLog() 函數(shù)將消息記錄到控制臺。它類似于標準的 C 庫中的 printf () 函數(shù),并采用可變數(shù)目的參數(shù),其中第一個必須是 Objective-C 字符串。 方法實現(xiàn)類似于 C 函數(shù)的定義,相關(guān)的代碼必須被大括號所包在內(nèi)。此外,方法的名稱必須與它的原型和參數(shù)和返回類型完全匹配。

Objective-C 從C語言中繼承屬性區(qū)分大小寫 ,所以此方法:

- (void)sayhello {
}

會被視為由編譯器作為完全不同于前面所示的 sayHello 方法。 一般情況下,方法名稱應(yīng)以小寫字母開頭。Objective-C 要求使用比你可能看到用于C中的函數(shù)更具描述性的名稱。如果方法名稱涉及到多個單詞,請使用棕色大小寫 (每個新單詞的首字母大寫),使它們易于閱讀。 請注意在 Objective-C 中那空白也是靈活的。習慣在代碼中使用制表符或空格,或者各個塊內(nèi)每一行的縮進,你會經(jīng)常看到左大括號位于單獨的一行,像這樣:

- (void)sayHello
{
  NSLog(@"Hello, World!");
}

Xcode,蘋果公司的集成開發(fā)環(huán)境 (IDE) 用于創(chuàng)建 OS X 和 iOS 軟件,它將基于一組自定義的用戶首選項自動縮進代碼。請參見更改縮進和制表符寬度Xcode Workspace Guide。 在下一章中你會看到更多例子的方法實現(xiàn),Working with Objects。

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

在Objective-C 中,類本身不透明的類型被稱為類的對象。類不能具有屬性以使用上文所示的實例,但他們的聲明語法定義可以接收消息。 類方法的典型用途是作為工廠模式,它替代對象分配和初始化步驟在對象創(chuàng)建中有所描述。例如,NSString 類,有各種各樣的工廠方法可用于創(chuàng)建一個空的字符串對象或使用特定的字符,包括初始化的字符串對象:

+ (id)string;
+ (id)stringWithString:(NSString *)aString;
+ (id)stringWithFormat:(NSString *)format, …;
+ (id)stringWithContentsOfFile:(NSString *)path encoding:(NSStringEncoding)enc error:(NSError **)error;
+ (id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc;

如這些示例中所示,類方法通過使用 + 表示,這讓他們從使用實例方法的標志中區(qū)別開來。 類方法原型可能包含在類接口中,就像實例方法原型。類方法實現(xiàn)與 @implementation 塊的類的實例方法方式相同。

練習 - Exercises

注: 為了跟著每章結(jié)尾給出的練習,您可能也希望創(chuàng)建 Xcode 項目。這將讓您確保您的代碼編譯沒有錯誤。

使用 Xcode 的新項目模板窗口從可用的 OS X 應(yīng)用程序項目模板創(chuàng)建一個命令行工具。出現(xiàn)提示時,指定為基礎(chǔ)的項目類型。

  1. 使用 Xcode 的新文件模板窗口創(chuàng)建名為的接口和實現(xiàn)文件。其中 XYZPerson 繼承于 NSObject。
  2. 向 XYZPerson 類接口添加屬性為一個人的名字、 姓氏和 出生日期的類。(日期由 NSDate 類描述) 的出生日期。
  3. 聲明 sayHello 的方法,并像前文所述那樣實現(xiàn)它。
  4. 添加一個聲明為類稱為"person"的工廠模式。不會不要緊,讀讀下一章!

注意:
如果您要編譯代碼,你可能會由于此缺少的實現(xiàn)而出現(xiàn) "Incomplete implementation" 警告。

以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號