iOS開發系列--Objective-C之類和物件

KenshinCui發表於2014-07-22

概述

前面已經簡單介紹過ObjC的基礎知識,讓大家對ObjC有個大致的印象,今天將重點解釋ObjC物件導向的特性。ObjC相對於C語言多了物件導向特性,但是ObjC又沒有其他面嚮物件語言那麼多語法特性,ObjC本身對物件導向進行了精簡。當然這並不代表今天的內容就會少,今天的內容還是相當多的:

  1. 類定義
  2. 成員變數
  3. 方法和屬性
  4. self關鍵字
  5. 構造方法
  6. description方法
  7. 繼承

類定義

在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的基類,所有的類最終都繼承於這個類(但是需要注意ObjC中的基類或者根類並不只有一個,例如NSProxy也是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中可訪問性修飾符除了這三種,還有一個@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中物件其實就是一個指標(例如之前看到的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;
}
@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屬性,那麼它們生成的規則是什麼呢,這裡總結如下:

  • 如果只宣告一個屬性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;
}

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

相關文章