概述
前面已經簡單介紹過ObjC的基礎知識,讓大家對ObjC有個大致的印象,今天將重點解釋ObjC面向對象的特性。ObjC相對於C語言多了面向對象特性,但是ObjC又沒有其他面向對象語言那麼多語法特性,ObjC本身對面向對象進行了精簡。當然這並不代表今天的內容就會少,今天的內容還是相當多的: 類定義 成員變量 方法和屬性 self關鍵字 構造方法 description方法 繼承 類定義 在C#、Java等其他高級語言中定義一個類是相當簡單點的,直接一個關鍵字class加一對大括號基本就完成了,但是在ObjC中類的定義相對變化比較大。現在假設需要定義一個Person類 在Xcode中添加文件,選擇Cocoa Class 或者Cocoa Touch Class cocoaClass 輸入類名Person,並選擇父類為NSObject ClassName 默認生成如下兩個文件 Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person @end 在ObjC中定義一個類需要兩個文件.h和.m: .h文件:放類的聲明,包括成員變量、屬性和方法聲明(事實上.h文件不參與編譯過程);關鍵字@interface聲明一個類,同時它必須以@end結束,在這兩個關鍵字中間聲明相關成員;在聲明Person類的同時可以看到它繼承於NSObject,這是ObjC的基類,所有的類最終都繼承於這個類,由於這個類在Foundation框架中定義,所以導入了<Foundation/Foundaton.h>(這麼描述的意思是導入Foundation框架中的Foundation.h聲明文件); .m文件:放屬性、方法的具體實現;關鍵字@implementation用於實現某個類,同時必須以@end結尾,在這兩個關鍵字中間實現具體的屬性、方法;由於.m中使用了Person類,所以需要導入聲明文件“Person.h”; 成員變量 假設在Person類中包含人員姓名(name)、年齡(age)、民族(nation)、身高(height)四個成員變量,同時姓名和年齡兩個成員變量是私有的,身高是公開的,民族則限制為只有子類可以訪問。 // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h>//由於使用了NSObject,所以導入此頭文件 //NSObject是基類,Person實現了NSObject @interface Person : NSObject{ /*成員變量必須包含在大括號中 *注意成員變量不聲明任何關鍵字的話是默認可訪問性@Protected *注意在ObjC中不管是自定義的類還是系統類對象都必須是一個指針,例如下面的_name */ @private NSString *_name;//在ObjC中推薦成員變量名以_開頭 int _age; @protected NSString *_nation; @public float height; } @end 成員變量定義在.h文件中,同時必須定義在類後面的{}內。成員的可訪問性通過下面三個關鍵字聲明: @private 私有成員,只有當前類可以訪問; @protected 受保護成員,只有當前類或子類可以訪問(如果沒有添加任何修飾則默認為@protected); @public 公共成員,所有類均可訪問; 在ObjC中可訪問性修飾符只有這三種,沒有類似於C#中的internal等關鍵字。那麼既然身高是公共的,外界怎麼訪問呢? // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p=[Person alloc]; p=[p init]; //上面兩句代碼可以直接寫成:Person *p=[[Person alloc] init]; //還可以寫成:Person *p=[Person new]; p->height=1.72; NSLog(@"height=%.2f",p->height);//結果:height=1.72 } return 0; } 這裡需要注意幾點: ObjC中所有的對象類型的變量都必須加上“*”,在ObjC中對象其實就是一個指針(例如之前看到的NSString也是如此,但是基本類型不用加”*”); ObjC中使用[]進行方法調用,在ObjC中方法調用的本質就是給這個對象或類發送一個消息; 在ObjC中類的實例化需要兩個步驟:分配內存、初始化; 類的初始化調用了父類的init方法,如果使用默認初始化方法進行初始化(沒有參數),內存分配和初始化可以簡寫成[Person new]; 公共成員的調用使用“->”操作符; 方法和屬性 既然有了上面成員變量,假設現在需要一個對象方法去設置用戶姓名,還需一個類方法打印一些信息。 在ObjC中方法分為靜態方法和動態方法兩種,動態方法就是對象的方法,靜態方法就是類方法,這一點跟其他高級語言沒有區別。在ObjC中使用“-”定義動態方法,使用“+”定義靜態方法。如果一個方法在.h中有聲明則該方法是公共方法,如果沒有在.h中聲明直接在.m中定義則該方法是私有方法,外部無法訪問。 person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h>//由於使用了NSObject,所以導入此頭文件 //NSObject是基類,Person實現了NSObject @interface Person : NSObject{ /*成員變量必須包含在大括號中 *注意成員變量不聲明任何關鍵字的話是@Protected,其他還有@Private和@Public *注意在ObjC中不管是自定義的類還是系統類對象都必須是一個指針,例如下面的_name */ @private NSString *_name;//在ObjC中推薦變量名以_開頭 int _age; @protected NSString *_nation; @public float height; } //聲明一個動態方法,沒有返回值 -(void)setName:(NSString *)name; //聲明一個靜態方法,沒有返回值 +(void)showMessage:(NSString *)info; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person //實現一個動態方法 -(void)setName:(NSString *)name{ _name=name; } //實現一個靜態方法 +(void)showMessage:(NSString *)info{ NSLog(@"%@",info); } @end 在ObjC中方法的參數類型、返回值類型需要放到()中,而且參數前必須使用冒號,並且此時冒號是方法名的一部分。當然,上面的方法只有一個參數,假設現在有一個方法可以同時設置年齡和籍貫,可以寫成如下形式: -(void)setAge:(int)age andHeight:(NSString *)nation{ _age=age; _nation=nation; } 其中andHeight可以省略不寫,當然為了保證方法名更有意義建議書寫時加上。 大家都知道在其他語言中還會經常提到屬性的概念,通常一個成員的訪問不會直接通過成員變量而是通過屬性暴漏給外界。在ObjC中屬性的實現方式其實類似於Java中屬性定義,通過對應的setter和getter方法進行實現。沒錯,上面setName其實就是屬性的setter方法,但是在ObjC中gettter方法通常使用變量名,而不加“get”。下面就看一下年齡屬性的實現 Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h>//由於使用了NSObject,所以導入此頭文件 //NSObject是基類,Person實現了NSObject @interface Person : NSObject{ /*成員變量必須包含在大括號中 *注意成員變量不聲明任何關鍵字的話是@Protected,其他還有@Private和@Public *注意在ObjC中不管是自定義的類還是系統類對象都必須是一個指針,例如下面的_name */ @private NSString *_name;//在ObjC中推薦變量名以_開頭 int _age; @protected NSString *_nation; @public float height; } //聲明一個動態方法,沒有返回值 -(void)setName:(NSString *)name; //聲明一個靜態方法,沒有返回值 +(void)showMessage:(NSString *)info; //聲明age的setter、getter方法 -(int)age; -(void)setAge:(int)age; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person //實現一個動態方法 -(void)setName:(NSString *)name{ _name=name; } //私有方法 -(void)setAge:(int)age andHeight:(NSString *)nation{ _age=age; _nation=nation; } //實現一個靜態方法 +(void)showMessage:(NSString *)info{ NSLog(@"%@",info); } //實現age的setter、getter方法 -(int)age{ return _age; } -(void)setAge:(int)age{ _age=age; } @end 接下來看一下具體的調用 // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { @autoreleasepool { Person *p=[Person alloc]; p=[p init]; //上面兩句代碼可以直接寫成:Person *p=[[Person alloc] init]; //還可以寫成:Person *p=[Person new]; //成員變量調用 p->height=1.72; NSLog(@"height=%.2f",p->height);//結果:height=1.72 //方法調用 [p setName:@"Kenshin"]; //屬性調用 p.age=28; //等價於:[p setAge:28]; int age=p.age;//等價於:age=[p age]; NSLog(@"age=%i",age); //結果:age=28 } return 0; } 關於方法的調用在這裡不著重介紹了,我們可以看到p.age的調用方式,是不是類似於C#、Java中屬性的調用方式,這就是ObjC中的點語法。其實這種方式調用的本質還是調用對應的方法進行處理,這麼做的目的只是為了開發人員書寫方便而已(這就是語法糖的目的)。至於p.age是調用get方法還是調用set方法完全取決於當前操作是賦值操作還是讀取操作。 通過上面的程序我們可以看到如果要定義一個屬性,首先需要在.h中聲明其次還要在.m中實現,而定義屬性的代碼基本都是類似的,那麼有沒有簡單的方法呢,其實在ObjC中可以通過聲明@property,同時通過@synthesize自動生成getter、setter方法(在新版本中甚至甚至都不用通過@synthesize只聲明就可以使用)。我們通過一段代碼來說明這個問題(為了方便大家查看代碼,在下面的代碼中暫時去掉前面定義的成員變量、屬性等) Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject{ @public NSString *birthday; NSString *_position; NSString *_degress; } @property NSString *birthday; @property NSString *position; @property NSString *degress; @property NSString *education; @property float weight; -(void)printInfo; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person @synthesize birthday; @synthesize position; @synthesize degress=_degress; @synthesize education; -(void)printInfo{ NSLog(@"_weight=%.2f",_weight); NSLog(@"education=%@",education); NSLog(@"_degress=%@",_degress); } @end main.m // // main.m // ClassAndObject // // int main(int argc, const char * argv[]) { // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *p=[[Person alloc]init]; p->birthday=@"1987-08-20"; p.birthday=@"1986-08-08"; p->_position=@"developer"; p.position=@"architect"; p.degress=@"undergraduate"; p.education=@"university"; p.weight=60.0; NSLog(@"p->birthday=%@,p.birthday=%@",p->birthday,p.birthday); //結果:p->birthday=1986-08-08,p.birthday=1986-08-08 NSLog(@"p->_position=%@,p.position=%@",p->_position,p.position); //結果:p->_position=developer,p.position=architect NSLog(@"p.weight=%.2f",p.weight); //結果:p.weight=60.00 [p printInfo]; /*結果: _weight=60.00 education=university _degress=undergraduate*/ return 0; } 通過上面的代碼我們可以看到最簡單的方法就是直接通過@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中又自定義實現了對應方法,則會使用自定義方法。 self關鍵字 在C#、Java中都有一個關鍵字this用於表示當前對象,其實在ObjC中也有一個類似的關鍵字self,只是self不僅可以表示當前對象還可以表示類本身,也就是說它既可以用在靜態方法中又可以用在動態方法中。 Perosn.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject @property NSString *name; @property int age; -(void)setName:(NSString *)name andAge:(int)age; +(void)showMessage; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person -(void)setName:(NSString *)name andAge:(int)age{ // _name=name; // _age=age; self.name=name; self.age=age; } +(void)printInfo{ NSLog(@"Hello,World!"); } +(void)showMessage{ [self printInfo]; } @end main.m // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *p=[[Person alloc]init]; [p setName:@"Kenshin" andAge:28]; [Person showMessage]; return 0; } 在上面代碼中可以看到setName: andAge:方法是一個動態方法,此時self就代表調用對象;而在showMessage方法中self調用了類的靜態方法printInfo,此時self代表調用的類;因此可以總結出在ObjC中self就代表當前方法的調用者。 擴展 前先看一段代碼 Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject @property NSString *name; @property int age; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person -(void)setName:(NSString *)name{ self.name=name; } @end main.m // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *p=[[Person alloc]init]; p.name=@"Kenshin"; return 0; } 如果運行上面的代碼將會發生死循環,原因很簡單,self.name=name本身就會調用Person的setName方法,如此反復就會造成循環操作,所有一般如果需要重寫setter方法,可以直接寫成_name=name,由此我們也可以看到為什麼之前即使沒有使用@property生成對應的屬性方法,在定義成員變量時也都加上了下劃線。 構造方法 在前面的代碼中我們已經看到如果要初始化一個類需要調用init方法,那麼下面看一下如何自定義構造方法 Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject @property NSString *name; @property int age; -(id)initWithName:(NSString *)name andAge:(int )age; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person //自定義構造方法 -(id)initWithName:(NSString *)name andAge:(int)age{ if(self=[super init]){ //super代表父類 self.name=name; self.age=age; } return self; } @end main.m // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *p=[[Person alloc]initWithName:@"Kenshin" andAge:28]; NSLog(@"name=%@,age=%i",p.name,p.age); //結果:name=Kenshin,age=28 return 0; } 在ObjC中super代表父類,通過調用父類的方法給當前對象賦值,然後判斷這個對象是否為nil,如果不為空則依次給name、age屬性賦值。 擴展 通過自定義構造方法固然可以簡化代碼,但是在使用時還要手動申請內存,在ObjC中一般我們通過定義一個靜態方法來解決這個問題 Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject @property NSString *name; @property int age; -(id)initWithName:(NSString *)name andAge:(int )age; +(id)personWithName:(NSString *)name andAge:(int )age; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person //自定義構造方法 -(id)initWithName:(NSString *)name andAge:(int)age{ if(self=[super init]){ //super代表父類 self.name=name; self.age=age; } return self; } //通過靜態方法獲得一個對象 +(id)personWithName:(NSString *)name andAge:(int)age{ Person *p=[[Person alloc]initWithName:name andAge:age]; return p; } @end main.m // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *p=[[Person alloc]initWithName:@"Kenshin" andAge:28]; NSLog(@"name=%@,age=%i",p.name,p.age); //結果:name=Kenshin,age=28 Person *p2=[Person personWithName:@"Kaoru" andAge:27]; NSLog(@"name=%@,age=%i",p2.name,p2.age); //結果:name=Kaoru,age=27 return 0; } description方法 在C#中每個類都有一個ToString()方法(java中叫做toString())用於打印一個對象的信息,在ObjC中這個方法叫description,例如在前面的Person類中我們可以重寫這個方法用於打印調試 Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person -(NSString *)description{ return [NSString stringWithFormat:@"{name:%@,age:%i}",self.name,self.age]; } @end main.m // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" int main(int argc, const char * argv[]) { Person *p=[[Person alloc]init]; p.name=@"Kenshin"; p.age=28; NSLog(@"%@",p);//此時會調用對象description方法返回對應的描述信息 /*結果: name:Kenshin,age:28} */ return 0; } 注意上面NSLog中的格式符是%@,當使用%@輸出一個對象時,ObjC會調用個對象的description返回對應的信息進行輸出,默認情況下如果我們不重寫description方法,輸出內容是類名和地址,例如Person則輸出“<Person: 0x100202310>”。 需要強調的是千萬不要在description中輸出self,因為當輸出self時會調用該對象的description方法,如此一來就會造成死循環。 繼承 繼承是面向對象三大特征之一,既然ObjC是面向對象語言,當然同樣支持繼承。事實上前面定義的Person類本身就繼承於NSObject,下面再簡單看一個例子,這裡部分假設我們還有一個Student類繼承於Person類,而且這個類有一個分數(score)屬性。 Person.h // // Person.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> @interface Person : NSObject{ @protected NSString *_nation; } #pragma mark - 屬性 #pragma mark 姓名 @property (nonatomic,copy) NSString *name; #pragma mark 年齡 @property (nonatomic,assign) int age; #pragma mark 籍貫 @property (nonatomic,copy) NSString *nation; #pragma mark - 動態方法 #pragma mark 帶有參數的構造函數 -(id)initWithName:(NSString *)name andAge:(int )age; #pragma mark - 靜態方法 #pragma mark 通過靜態方法返回一個對象 +(id)personWithName:(NSString *)name andAge:(int )age; @end Person.m // // Person.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @implementation Person #pragma mark - 動態方法 #pragma mark 帶有參數的構造函數 -(id)initWithName:(NSString *)name andAge:(int)age{ if(self=[super init]){ //super代表父類 self.name=name; self.age=age; } return self; } #pragma mark - 靜態方法 #pragma mark 通過靜態方法返回一個對象 +(id)personWithName:(NSString *)name andAge:(int)age{ Person *p=[[Person alloc]initWithName:name andAge:age]; return p; } #pragma mark - 重寫方法 #pragma mark 重寫description -(NSString *)description{ return [NSString stringWithFormat:@"{name:%@,age:%i}",self.name,self.age]; } @end Student.h // // Student.h // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Person.h" @interface Student : Person #pragma mark - 屬性 #pragma mark 分數 @property (nonatomic,assign) float score; #pragma mark - 動態方法 #pragma mark 帶有參數的構造函數 -(id)initWithName:(NSString *)name andAge:(int )age andScore:(float)score; #pragma mark - 靜態方法 #pragma mark 通過靜態方法返回一個對象 +(id)studentWithName:(NSString *)name andAge:(int )age andScore:(float)score; @end Student.m // // Student.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import "Student.h" @implementation Student #pragma mark - 動態方法 #pragma mark 帶有參數的構造函數 -(id)initWithName:(NSString *)name andAge:(int )age andScore:(float)score{ if(self=[super initWithName:name andAge:age]){ self.score=score; } return self; } #pragma mark - 靜態方法 #pragma mark 通過靜態方法返回一個對象 +(id)studentWithName:(NSString *)name andAge:(int)age andScore:(float)score{ Student *s=[[Student alloc]initWithName:name andAge:age andScore:score]; return s; } #pragma mark - 重寫方法 #pragma mark 重寫description -(NSString *)description{ return [NSString stringWithFormat:@"{name:%@,age:%i,nation:%@,scroe:%.2f}",self.name,self.age,self->_nation,self.score]; //注意這裡訪問了父類的屬性和方法 } @end main.m // // main.m // ClassAndObject // // Created by Kenshin Cui on 14-2-1. // Copyright (c) 2014年 Kenshin Cui. All rights reserved. // #import <Foundation/Foundation.h> #import "Person.h" #import "Student.h" int main(int argc, const char * argv[]) { Person *p=[Person personWithName:@"Kenshin" andAge:28]; NSLog(@"p=%@",p); Student *s=[Student studentWithName:@"Kaoru" andAge:27 andScore:100]; s.nation=@"henan"; NSLog(@"s=%@",s); return 0; } 繼承知識比較簡單,通過上面的代碼基本上就可以了解,這裡不做詳細論述