天天看點

iOS開發系列--Objective-C之類和對象概述類定義成員變量方法和屬性self關鍵字構造方法description方法繼承

前面已經簡單介紹過objc的基礎知識,讓大家對objc有個大緻的印象,今天将重點解釋objc面向對象的特性。objc相對于c語言多了面向對象特性,但是objc又沒有其他面向對象語言那麼多文法特性,objc本身對面向對象進行了精簡。當然這并不代表今天的内容就會少,今天的内容還是相當多的:

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#class">類定義</a>

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#field">成員變量</a>

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#method">方法和屬性</a>

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#self">self關鍵字</a>

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#constructor">構造方法</a>

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#description">description方法</a>

<a href="http://www.cnblogs.com/kenshincui/p/3861302.html#inherit">繼承</a>

在c#、java等其他進階語言中定義一個類是相當簡單點的,直接一個關鍵字class加一對大括号基本就完成了,但是在objc中類的定義相對變化比較大。現在假設需要定義一個person類

在xcode中添加檔案,選擇cocoa class 或者cocoa touch class

iOS開發系列--Objective-C之類和對象概述類定義成員變量方法和屬性self關鍵字構造方法description方法繼承

輸入類名person,并選擇父類為nsobject

iOS開發系列--Objective-C之類和對象概述類定義成員變量方法和屬性self關鍵字構造方法description方法繼承

person.h

person.m

在objc中定義一個類需要兩個檔案.h和.m:

.h檔案:放類的聲明,包括成員變量、屬性和方法聲明(事實上.h檔案不參與編譯過程);關鍵字@interface聲明一個類,同時它必須以@end結束,在這兩個關鍵字中間聲明相關成員;在聲明person類的同時可以看到它繼承于nsobject,這是objc的基類,所有的類最終都繼承于這個類(但是需要注意objc中的基類或者根類并不隻有一個,例如nsproxy也是objc的基類),由于這個類在foundation架構中定義,是以導入了&lt;foundation/foundaton.h&gt;(這麼描述的意思是導入foundation架構中的foundation.h聲明檔案);

.m檔案:放屬性、方法的具體實作;關鍵字@implementation用于實作某個類,同時必須以@end結尾,在這兩個關鍵字中間實作具體的屬性、方法;由于.m中使用了person類,是以需要導入聲明檔案“person.h”;

假設在person類中包含人員姓名(name)、年齡(age)、民族(nation)、身高(height)四個成員變量,同時姓名和年齡兩個成員變量是私有的,身高是公開的,民族則限制為隻有子類可以通路。

成員變量定義在.h檔案中,同時必須定義在類後面的{}内。成員的可通路性通過下面三個關鍵字聲明:

@private 私有成員,隻有目前類可以通路;

@protected 受保護成員,隻有目前類或子類可以通路(如果沒有添加任何修飾則預設為@protected);

@public 公共成員,所有類均可通路;

在objc中可通路性修飾符除了這三種,還有一個@package不太常用,它類似于c#中的internal在架構内是公共的,但是架構外是私有的(也就是隻能在一個架構内可以通路)。那麼既然身高是公共的,外界怎麼通路呢?

這裡需要注意幾點:

objc中所有的對象類型的變量都必須加上“*”,在objc中對象其實就是一個指針(例如之前看到的nsstring也是如此,但是基本類型不用加”*”);

objc中使用[]進行方法調用,在objc中方法調用的本質就是給這個對象或類發送一個消息;

在objc中類的執行個體化需要兩個步驟:配置設定記憶體、初始化;

類的初始化調用了父類的init方法,如果使用預設初始化方法進行初始化(沒有參數),記憶體配置設定和初始化可以簡寫成[person new];

公共成員的調用使用“-&gt;”操作符;

既然有了上面成員變量,假設現在需要一個對象方法去設定使用者姓名,還需一個類方法列印一些資訊。

在objc中方法分為靜态方法和動态方法兩種,動态方法就是對象的方法,靜态方法就是類方法,這一點跟其他進階語言沒有差別。在objc中使用“-”定義動态方法,使用“+”定義靜态方法。如果一個方法在.h中有聲明則該方法是公共方法,如果沒有在.h中聲明直接在.m中定義則該方法是私有方法,外部無法通路。

在objc中方法的參數類型、傳回值類型需要放到()中,而且參數前必須使用冒号,并且此時冒号是方法名的一部分。當然,上面的方法隻有一個參數,假設現在有一個方法可以同時設定年齡和籍貫,可以寫成如下形式:

其中andheight可以省略不寫,當然為了保證方法名更有意義建議書寫時加上。

大家都知道在其他語言中還會經常提到屬性的概念,通常一個成員的通路不會直接通過成員變量而是通過屬性暴漏給外界。在objc中屬性的實作方式其實類似于java中屬性定義,通過對應的setter和getter方法進行實作。沒錯,上面setname其實就是屬性的setter方法,但是在objc中gettter方法通常使用變量名,而不加“get”。下面就看一下年齡屬性的實作

接下來看一下具體的調用

關于方法的調用在這裡不着重介紹了,我們可以看到p.age的調用方式,是不是類似于c#、java中屬性的調用方式,這就是objc中的點文法。其實這種方式調用的本質還是調用對應的方法進行處理,這麼做的目的隻是為了開發人員書寫友善而已(這就是文法糖的目的)。至于p.age是調用get方法還是調用set方法完全取決于目前操作是指派操作還是讀取操作。

通過上面的程式我們可以看到如果要定義一個屬性,首先需要在.h中聲明其次還要在.m中實作,而定義屬性的代碼基本都是類似的,那麼有沒有簡單的方法呢,其實在objc中可以通過聲明@property,同時通過@synthesize自動生成getter、setter方法(在新版本中甚至甚至都不用通過@synthesize隻聲明就可以使用)。我們通過一段代碼來說明這個問題(為了友善大家檢視代碼,在下面的代碼中暫時去掉前面定義的成員變量、屬性等)

main.m

上面的代碼雖然簡單,但是幾乎涵蓋所有屬性生成規則。通過上面的代碼我們可以看到最簡單的方法就是直接通過@property就可以聲明一個變量(例如weight屬性),不需要進行實作即可直接使用;還可以使用@property聲明再用@synthesize去實作(例如上面的birthday屬性),不僅如此在實作的時候還可以指定實作此屬性時使用哪個成員變量(例如degress屬性)。在上面的代碼中我們還看到weight屬性自動生成了一個_weight成員變量,而education生成了一個education屬性,那麼它們生成的規則是什麼呢,這裡總結如下:

如果隻聲明一個屬性a,不使用@synthesize實作:編譯器會使用_a作為屬性的成員變量(如果沒有定義成員變量_a則會自動生成一個私有的成員變量_a;如果已經定義了成員變量_a則使用自定義的成員變量_a。注意:如果此時定義的成員變量不是_a而是a則此時會自動生成一個成員變量_a,它跟自定義成員變量a沒有任何關系);

如果聲明了一個屬性a,使用@synthesize a進行實作,但是實作過程中沒有指定使用的成員變量(例如上面birthday):則此時編譯器會使用a作為屬性的成員變量(如果定義了成員變量a,則使用自定義成員變量;如果此時沒有定義則會自動生成一個私有的成員變量a,注意如果此時定義的是_a則它跟生成的a成員變量沒有任何關系);

如果聲明了一個屬性a,使用@synthesize a=_a進行實作,這個過程已經指定了使用的成員變量:此時會使用指定的成員變量作為屬性變量;

有了上面的總結,相信了解上面的代碼并不難,通常在實際開發過程中我們要麼直接在@property中聲明不使用@synthesize;要麼使用過程中指定具體的成員變量。

此外再次強調一下,通過上面的方式定義變量的本質還是生成對應的gettter、setter方法(隻是這個步驟編譯器幫你完成了),如果通過@property定義了屬性,同時在.m中又自定義實作了對應方法,則會使用自定義方法。

在c#、java中都有一個關鍵字this用于表示目前對象,其實在objc中也有一個類似的關鍵字self,隻是self不僅可以表示目前對象還可以表示類本身,也就是說它既可以用在靜态方法中又可以用在動态方法中。

perosn.h

在上面代碼中可以看到setname: andage:方法是一個動态方法,此時self就代表調用對象;而在showmessage方法中self調用了類的靜态方法printinfo,此時self代表調用的類;是以可以總結出在objc中self就代表目前方法的調用者。

先看一段代碼

如果運作上面的代碼将會發生死循環,原因很簡單,self.name=name本身就會調用person的setname方法,如此反複就會造成循環操作,所有一般如果需要重寫setter方法,可以直接寫成_name=name,由此我們也可以看到為什麼之前即使沒有使用@property生成對應的屬性方法,在定義成員變量時也都加上了下劃線(這是一好的編碼習慣)。

在前面的代碼中我們已經看到如果要初始化一個類需要調用init方法,那麼下面看一下如何自定義構造方法

在objc中super代表父類,通過調用父類的方法給目前對象指派,然後判斷這個對象是否為nil,如果不為空則依次給name、age屬性指派。

通過自定義構造方法固然可以簡化代碼,但是在使用時還要手動申請記憶體,在objc中一般我們通過定義一個靜态方法來解決這個問題

在c#中每個類都有一個tostring()方法(java中叫做tostring())用于列印一個對象的資訊,在objc中這個方法叫description,例如在前面的person類中我們可以重寫這個方法用于列印調試

注意上面nslog中的格式符是%@,當使用%@輸出一個對象時,objc會調用個對象的description傳回對應的資訊進行輸出,預設情況下如果我們不重寫description方法,輸出内容是類名和位址,例如person則輸出“&lt;person: 0x100202310&gt;”。

需要強調的是千萬不要在description中列印輸出self,因為當輸出self時會調用該對象的description方法,如此一來就會造成死循環。

繼承是面向對象三大特征之一,既然objc是面向對象語言,當然同樣支援繼承。事實上前面定義的person類本身就繼承于nsobject,下面再簡單看一個例子,這裡部分假設我們還有一個student類繼承于person類,而且這個類有一個分數(score)屬性。

student.h

student.m

繼承知識比較簡單,通過上面的代碼基本上就可以了解,這裡不做詳細論述。