前面已經簡單介紹過ObjC的基礎知識,讓你們對ObjC有個大體的印象,今天將重點解釋ObjC面向對象的特性。ObjC相對於C語言多了面向對象特性,可是ObjC又沒有其餘面嚮對象語言那麼多語法特性,ObjC自己對面向對象進行了精簡。固然這並不表明今天的內容就會少,今天的內容仍是至關多的:java
在C#、Java等其餘高級語言中定義一個類是至關簡單點的,直接一個關鍵字class加一對大括號基本就完成了,可是在ObjC中類的定義相對變化比較大。如今假設須要定義一個Person類框架
在Xcode中添加文件,選擇Cocoa Class 或者Cocoa Touch Class函數
輸入類名Person,並選擇父類爲NSObjectui
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.matom
// // 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:spa
假設在Person類中包含人員姓名(name)、年齡(age)、民族(nation)、身高(height)四個成員變量,同時姓名和年齡兩個成員變量是私有的,身高是公開的,民族則限制爲只有子類能夠訪問。3d
// // 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文件中,同時必須定義在類後面的{}內。成員的可訪問性經過下面三個關鍵字聲明:指針
在ObjC中可訪問性修飾符除了這三種,還有一個@package不太經常使用,它相似於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中使用「-」定義動態方法,使用「+」定義靜態方法。若是一個方法在.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; } @en
接下來看一下具體的調用
// // 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屬性,那麼它們生成的規則是什麼呢,這裏總結以下:
有了上面的總結,相信理解上面的代碼並不難,一般在實際開發過程當中咱們要麼直接在@property中聲明不使用@synthesize;要麼使用過程當中指定具體的成員變量。
此外再次強調一下,經過上面的方式定義變量的本質仍是生成對應的gettter、setter方法(只是這個步驟編譯器幫你完成了),若是經過@property定義了屬性,同時在.m中又自定義實現了對應方法,則會使用自定義方法。
在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; }
在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; }
繼承知識比較簡單,經過上面的代碼基本上就能夠了解,這裏不作詳細論述。