繼承性是面向對象的重要概念之一, 子類能夠繼承父類的某些方法和成員變量。?
作用域限定符為private的成員變量是不可以被繼承的。?
子類還可以重寫父類的方法。?
當然,這一切要從根類開始:
沒有父類的類,位于類層次結構的最頂層,稱為根(Root)類。
NSObject是層次結構的最頂端(也就是它上面沒有任何類),因此稱為根類。
如果使用術語,可以將類稱為子類和父類。同樣,也可以將類稱為子類和超類。
需要注意的是,要在子類中直接使用實例變量,必須先在接口部分聲明。
在實現部分聲明和合成(synthesize)的實例變量是私有的,子類中并不能夠直接訪問,
需要明確定義或合成取值方法,才能訪問實例變量的值。
繼承的概念作用于整個繼承鏈。
一定要理解以下事實:類的每個實例都擁有自己的實例變量,即使這些實例變量是繼承來的。
找出正確的方法:
首先,檢查該對象所屬的類,以查看在該類中是否明確定義了一個具有指定名稱的方法。
如果有,就使用這個方法。如果沒有定義,就檢查它的父類。
如果父類中有定義,就使用這個方法,否者,繼續找尋。
直到找到根類也沒有發現任何方法。
通過繼承來擴展:添加新方法
繼承通常用于擴展一個類。
@class指令:
@class XYPoint;
或
~~~
#import "XYPoint.h"
~~~
使用@class指令提高了效率,因為編譯器不需要引入和處理整個XYPoint.h文件(雖然它很小),
只需要知道XYPoint是一個類名。
如果需要引用XYPoint類的方法(在實現部分中),@class指令是不夠的,因為編譯器需要更多的消息。
說的通俗點:只引用了類就用@class不然就用#import。
在默認情況下,合成的設值方法只是簡單地復制對象指針,而不是對象本身。
你可以合成另一種設值方法,而不是制作對象的副本。
為了了解繼承性, 我們看看這樣的一個場景:?
一位剛學習面向對象的小菜,自從當上了班長,他就有的忙了,因為錄入檔案,需要描述和處理個人信息,
于是他定義了類Person:
~~~
@interface Person: NSObject {
NSString* name;
int age;
NSDate birthDate;
}
-(NSString*) getInfo;
@end
~~~
新的校花School Beauty類:
一周以后, 小菜又遇到了新的需求, 他的幾個表妹非要把各自學校的校花介紹給我他,煩惱呀!
需要描述和處理校花信息, 于是他又定義了一個新的類Beauty。?
~~~
@interface?Beauty: NSObject {
NSString* name;
int age;
NSDate birthDate;
NSString* school;
}
-(NSString*) getInfo;
@end
~~~
#### 小結
Beauty和Person兩個類的結構太接近了,?
后者只比前者多出一個屬性school , 卻要重復定義其它所有的內容。
Objective-C提供了解決類似問題的機制, 那就是類的繼承。?
@interface?Beauty: Person {
NSString* school;
}
方法重寫或者說是覆寫方法:
不能通過繼承刪除或減少方法,但可以利用覆寫來更改繼承方法的定義。
新方法必須具有相同的返回類型,并且參數的數目與覆寫的方法相同。
如果在不同的類中有名稱相同的方法,則根據作為消息的接收者的類選擇正確的方法。
#### 為什么要創建子類?
有如下3個理由:
1)希望繼承一個類的方法,也許加入一些新的方法和或實例變量。
2)希望創建一個類的特別的版本。
3)希望通過覆寫一個或多個方法來改變類的默認行為。
抽象類:
有時,創建類只是為了更容易創建子類。
因此,這些類名為抽象(abstract)類,或等價地稱為抽象超類(abstract superclasses)。
在該類中定義方法和實例變量,但不期望任何人從該類創建實例。
注意:
子類不能繼承父類中作用域限定符為@private的成員變量。?
子類可以重寫父類的方法,及命名與父類同名的成員變量。?
下面再通過一個矩形類和正方形類的實例說明方法重寫問題:
Rectangle.h文件:
~~~
#import???
??
@interface?Rectangle:?NSObject?{??
????int?width;??
????int?height;??
}??
??
-(Rectangle*)?initWithWidth:?(int)?w?height:?(int)?h;??
-(void)?setWidth:?(int)?w;??
-(void)?setHeight:?(int)?h;??
-(void)?setWidth:?(int)?w?height:?(int)?h;??
-(int)?width;??
-(int)?height;??
-(void)?print;??
@end??
~~~
Rectangle.m文件:
~~~
#import?"Rectangle.h"??
??
@implementation?Rectangle??
??
-(Rectangle*)?initWithWidth:?(int)?w?height:?(int)?h?{??
????self?=?[super?init];??
??
????if?(?self?)?{??
????????[self?setWidth:?w?height:?h];??
????}??
??
????return?self;??
}??
??
-(void)?setWidth:?(int)?w?{??
????width?=?w;??
}??
??
-(void)?setHeight:?(int)?h?{??
????height?=?h;??
}??
??
-(void)?setWidth:?(int)?w?height:?(int)?h?{??
????width?=?w;??
????height?=?h;??
}??
??
-(int)?width?{??
????return?width;??
}??
??
-(int)?height?{??
????return??height;??
}??
??
-(void)?print?{??
????NSLog(@"width?=?%i,?height?=?%i",?width,?height?);??
}??
@end??
~~~
Square.h文件:
~~~
#import?"Rectangle.h"??
??
@interface?Square:?Rectangle??
-(Square*)?initWithSize:?(int)?s;??
-(void)?setSize:?(int)?s;??
-(int)?size;??
@end??
~~~
Square.m文件:
~~~
#import?"Square.h"??
??
@implementation?Square??
-(Square*)?initWithSize:?(int)?s?{??
????self?=?[super?init];??
??
????if?(?self?)?{??
????????[self?setSize:?s];??
????}??
??
????return?self;??
}??
??
-(void)?setSize:?(int)?s?{??
????width?=?s;??
????height?=?s;??
}??
??
-(int)?size?{??
????return?width;??
}??
??
-(void)?setWidth:?(int)?w?{??
????[self?setSize:?w];??
}??
??
-(void)?setHeight:?(int)?h?{??
????[self?setSize:?h];??
}??
@end??
~~~
調試用的main函數:
~~~
#import???
#import?"Square.h"??
#import?"Rectangle.h"??
??
int?main?(int?argc,?const?charchar?*?argv[])?{??
??
????????Rectangle?*rec?=?[[Rectangle?alloc]?initWithWidth:?10?height:?20];??
????Square?*sq?=?[[Square?alloc]?initWithSize:?15];??
??
????NSLog(@"Rectangle:?"?);??
????[rec?print];??
??????
????????NSLog(@"Square:?"?);??
????[sq?print];??
??????
????[sq?setWidth:?20];??
????NSLog(@"Square?after?change:?"?);??
????[sq?print];??
??
????[rec?release];??
????[sq?release];??
??????????
????return?0;??
}??
~~~
運行結果:
~~~
Rectangle:
width = 10, height = 20
Square:
width = 15, height = 15
Square after change:
width = 20, height = 20
~~~