代码改变世界

【转】Objective-C学习笔记九:继承一

2013-03-10 17:49  maying_07  阅读(141)  评论(0编辑  收藏  举报

原文地址:http://sarin.iteye.com/blog/1768779

  继承是面向对象的一个核心概念。在Objective-C的继承体系中,位于最顶层的根类是NSObject,类比Java中的java.lang.Object类,我们定义的所有类都是它的子类。子类也叫扩展类或派生类。 
    我们之前使用的分数类Fraction就是NSObject类的派生类。继承使得子类可以从父类中获得一些属性和已有方法。要注意的是如果子类中要直接使用父类继承过来的实例变量,那么必须将变量声明在接口部分中,而在实现部分声明的变量,子类无法继承使用。在实现部分声明和synthesize的实例变量都是私有的,子类不能直接访问,需要提供设置值和取值方法才可以访问这些变量。 
    看下面这个例子,我们来具体看看继承的实现: 

 

 1 #import <Foundation/Foundation.h>
 2 @interface ClassA:NSObject
 3 {
 4     int n;
 5 }
 6 -(void) initVar;
 7 @end
 8 
 9 @implementation ClassA
10 -(void) initVar
11 {
12     n=406;
13 }
14 @end
15 
16 @interface ClassB:ClassA
17 -(void) printVar;
18 @end
19 
20 @implementation ClassB
21 -(void) printVar
22 {
23     NSLog(@"n=%i",n);
24 }
25 @end
26 
27 int main(int argc, const char * argv[])
28 {
29     @autoreleasepool{
30         ClassB *clsB=[ClassB new];
31         [clsB initVar];
32         [clsB printVar];
33     }
34     return 0;
35 }

 

编译运行后,我们得到如下结果: 

 

    下面来解释这个程序,该程序中我们定义了两个类ClassA和ClassB,ClassA的父类是NSObject,这是Objective-C中的根类,ClassB继承自ClassA,那么ClassB也间接继承了NSObject类。定义ClassA的接口时,我们定义了一个变量n,那么子类ClassB才能访问到它,ClassA中定义了方法initVar,而ClassB中定义了方法printVar。在执行主函数时,首先创建了ClassB对象,使用initVar来初始化参数,initVar是ClassA中的方法,此时使用ClassB类的引用指针来调用就体现了继承的观点。然后再执行printVar方法,我们就看到了结果。 
    这里我们使用new来创建对象,是我们用它来代替alloc和init方法,这两个方法我们没有定义却直接使用了,说明它是从根类NSObject中继承而来的。之前代码中的示例也一直使用到了继承。 
    我们将代码修改如下: 

 

 1 #import <Foundation/Foundation.h>
 2 @interface ClassA:NSObject
 3 {
 4     int n;
 5 }
 6 -(void) initVar;
 7 -(void) setVar:(int) m;
 8 -(void) print;
 9 @end
10 
11 @implementation ClassA
12 -(void) initVar
13 {
14     n=406;
15 }
16 -(void) setVar:(int) m
17 {
18     n=m;
19 }
20 -(void) print
21 {
22     NSLog(@"n=%i",n);
23 }
24 @end
25 
26 @interface ClassB:ClassA
27 -(void) printVar;
28 @end
29 
30 @implementation ClassB
31 -(void) printVar
32 {
33     NSLog(@"n=%i",n);
34 }
35 @end
36 
37 int main(int argc, const char * argv[])
38 {
39     @autoreleasepool{
40         ClassA *clsA=[[ClassA alloc] init];
41         ClassB *clsB=[ClassB new];
42         [clsB initVar];
43         [clsB printVar];
44 
45         [clsA setVar:10];
46         [clsA print];
47         [clsB printVar];
48 
49 
50     }
51     return 0;
52 }

 

再次编译运行,我们得到如下输出: 

 

    这里我们在主函数中创建了对象ClassA,并且为A提供了设置值和打印方法。那么程序中间我们重新设置了ClassA的属性n,然后再打印值来看。那么这个结果告诉我们一个事实:类的每个实例都有自己的实例变量,即便这些变量是继承来的,那么对于父类中的变量修改,子类中的值无影响。ClassB和ClassA拥有完全不同的实例变量。 
    下面我们继续来深入理解继承,首先设计一个矩形类Rectangle,代码如下: 

 1 #import <Foundation/Foundation.h>
 2 
 3 @interface Rectangle : NSObject
 4 
 5 @property int width,height;
 6 
 7 -(int) area;
 8 -(int) perimeter;
 9 -(void) setWidth:(int) w andHeight:(int) h;
10 @end

 

  实现文件如下: 

 

 1 #import "Rectangle.h"
 2 
 3 @implementation Rectangle
 4 
 5 @synthesize width, height;
 6 
 7 -(int) area
 8 {
 9     return width*height;
10 }
11 
12 -(int) perimeter
13 {
14     return (width+height)*2;
15 }
16 
17 -(void) setWidth:(int)w andHeight:(int)h
18 {
19     width=w;
20     height=h;
21 }
22 @end

 

主函数如下: 

 1 #import "Rectangle.h"
 2 
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         Rectangle *rect=[Rectangle new];
 8         
 9         [rect setWidth:10 andHeight:23];
10         
11         NSLog(@"Rectangle: width=%i, height=%i",rect.width,rect.height);
12         NSLog(@"Area = %i, Perimeter = %i",rect.area,rect.perimeter);
13     }
14     return 0;
15 }

 编译运行,得到如下结果: 

 

    这个矩形类的定义很简单,定义了长和宽两个变量,定义了设置长和宽的方法,求面积方法和求周长方法。直接运行即可得到结果并不复杂。如果我们需要一个正方形类,该如何设计呢?正方形是矩形的一个特殊情况,就是长宽相等,那么可以将正方形类视作矩形类的子类,那么我们创建这个类,并编写如下代码:

 

1 #import "Rectangle.h"
2 
3 @interface Square : Rectangle
4 
5 -(void) setSide:(int) s;
6 -(int) side;
7 @end

 

   编写正方形类Square的实现: 

 1 #import "Square.h"
 2 
 3 @implementation Square
 4 
 5 -(void) setSide:(int)s
 6 {
 7     [self setWidth:s andHeight:s];
 8 }
 9 
10 -(int) side
11 {
12     return self.width;
13 }
14 @end

编写测试的主函数: 

 

 1 #import "Square.h"
 2 
 3 int main(int argc, const char * argv[])
 4 {
 5 
 6     @autoreleasepool {
 7         Square *square = [Square new];
 8         
 9         [square setSide:10];
10         
11         NSLog(@"Square: s=%i",[square side]);
12         NSLog(@"Area = %i, Perimeter = %i",square.area,square.perimeter);
13     }
14     return 0;
15 }

 

 编译运行,我们得到如下结果: 

 

    这里我们定义的Square类继承了Rectangle类,并定义了setSide和side方法用于设置边长和返回边长,那么在实现方法中。setSide方法使用了self关键字调用了父类的setWidth addHeight方法,这也是继承的体现。那么side 方法返回边长即可,注意这里的self.width其实是[self width],父类中我们使用了@synthesize指令,那么对私有属性width就提供了访问方法,而方法名和属性名同名,但是要清楚这里我们是调用的方法而不是属性,这点理解是很重要的。