Objective-c 知识总结 -- 继承

2016-05-07  本文已影响133人  半纸渊

知识盲点:


知识点:


圆(.h):


方形(.h):



观察发现,它们属性和方法声明是相同的,都有 填充色(fillcolor)、尺寸+位置(bounds)、绘制方法;

如此相同的声明,除了类名不同,其它都一样?那么可否把它们的声明封装(抽象)起来,但类名(具体细节)又可以不一样?
先保存这种疑问,来看看(.m)文件。

圆(.m):


方形(.m):


观察发现,圆和方形的 setFillColor: 和 setBounds: 方法的实现是一样的,唯一的不同就是它们的绘制方法 draw: ;

通过观察分析可以得知,如果要把圆、方形封装起来,那么就要保证具体的实现可以有不一样(draw:)方法;简单来说就是,封装(抽象)不对细节进行限制,只对声明进行限制,就是只告诉你叫什么,不告诉你是什么,要确定是什么,根据不同的类型(圆或者方形)来确定。

结:我们都知道的,圆和方形都是几何图形,而面向对象编程,就是对现实世界的抽象,而圆和方形的抽象就是几何图形;换种方式描述就是,几何图形是圆、方形的父集(父亲),圆、方形是几何的子集(子女)。

抽象过程:


继承 :Inheritance in OOP means that a class acquires features from another class, its parent or superclass.(一个类(子类)的特性(属性+方法+协议......)来源于另一个类(父类))

首先,通过上面的分析,圆、方形的特性可以由几何图形来提供,也就是说圆、方形是可以继承于几何图形的。


分析代码:

@interface 子类 : 父类
// 新特性
@end

@interface 和 @end 是一对,不能拆开前者表明继承的开始,后者表明继承的结束;
子类,就是新创建的类的类名(Circle / Rectangle);

父类,要继承的类(Shape);
//新特性:可以定义自己的特性
上面 代码的意思:Circle/Rectangle继承于Shape(拥有Shape的特性);

Shape(.h):


Shape(.m):


代码分析:
@interface Shape : NSObject ,NSObject是 Cocoa Touch 框架的根类,所有类的父类;

{...},是声明实例变量;

@implementaion 和 @end 是一对,表明对方法的实现;
观察 Shape 的(.m)文件可以发现,只有 draw 方法是空的,因为我们清楚圆和方形的绘制方式是不同的(相当于等待子类自己去实现),而颜色填充和尺寸位置的表现方式是一样的;

圆的绘制方法:


方形的绘制方法:


疑问:

一些继承的术语:

疑问解答:
1.Objective-C不能实现多继承,就是说(class : class1,class2...)是不允许的;

2.父类的实例变量能否被子类使用,取决于实例变量的权限修饰符


默认是@protected,子类可以继承父类的实例变量,但是是否可以访问,就看权限修饰符;

如果是使用属性@property进行声明的,就要查看相应的属性修饰符;

3.方法调度优先级:

When code sends a message, the Objective-C method dispatcher searches for the method in the current class. If the dispatcher doesn’t find the method in the class of the object receiving the message, it looks at the object’s superclasses.(当一个类发送消息的时候,调度器会首先从当前类中的方法列表中查找相应的消息方法,如果发现当前没有找到,就会进入到当前类的父类中进行查找如果有就执行,如果没有就继续向父类查找直到找到 NSObject 类还是没有的话,就直接报错。)

例子:
假设给圆这个类的实例设置颜色如下:
[Circle setFillColor:kRedColor];

未继承Shape

继承了Shape


4.子类可以添加新的实例变量

假设创建一个新类:RoundedRectangle(圆角矩形)
首先它是几何图形,也是矩形(方形),但是比矩形多了一个圆角;所以它可以直接继承几何图形,也可以继承矩形;

创建一个 RoundedRectangle 对象实例,它的实例变量在内存的分布是:

内容分析:
“isa”:The NSObject instance variable is called isa because inheritance sets up an “is a” relationship between the subclass and the superclass; that is, a Rectangle is a Shape, and a Circleis a Shape. Code that uses a Shape can also use a Rectangle or Circle instead.(isa意指 “是一个” ,如:圆是一个几何图形,矩形是一个几何图形,表明一种包含关系);isa 是 NSObject 的实例变量;

“fillcolor bounds”:Shape 的实例变量,因为 RoundedRectangle 继承了 Shape 所以有这两个特性,而 fillcolor 是先于 bounds 被定义的,所以它处于上方;
“radius”:圆角是 RoundedRectangle 这个类特有的特性,因为最后被定义所以处于最后的位置;

注:每一个实例变量都有一个隐藏的实例(元类的实例) self

完整的图是:


内容分析:
所有的实例变量是分配在一块内存区域中的,而且是有序的、每一个实例变量的内存大小是已经固定的;

self 就是指向内存区域的首地址,只要根据各个实例变量的内存大小进行移位就可以正常访问到每一个实例变量;(The compiler works its magic by using a “base plus offset” (首地址+偏移量)mechanism. Given the base address of an object—that is, the memory location of the first byte of the first instance variable—the compiler can find all other instance variables by adding an offset to that address.)

???问题:This does lead to problems over time. These offsets are now hard-coded into the program generated by the compiler. Even if Apple’s engineers wanted to add another instance variable to NSObject, they couldn’t, because that would change all of the instance variable offsets. This is called the fragile base class problem(脆弱的基类问题). Apple has fixed this problem with the new 64-bit Objective-C runtime introduced with Leopard, which uses indirection for determining ivar locations.

从两张图可以知道,当一个类的实例化后,它的实例对象在内存的位置(地址)是固定的,而且大小也是固定的,也就是 self 每一次的偏移量也是固定的;

那么问题来了,假设我现在又想增加一个实例变量呢,如果是添加在 radius 的后面,内存的地址没有发生变化,如果添加在 fillcolor / bounds / radius 的中间呢?那么内存地址就发生了改变, self 的每一交偏移量就发生了改变;

所以在后来苹果使用了间接的手段对 ivar (实例变量)进行内存测定,从而杜绝实例对象在初始化化后实例变量频繁修改所引起的内存变化;

5.修改(重写)父类的方法(特性)

When classes such as Circle and Rectangle implement their own draw methods, we say that they have overridden the draw method.

在文章的开始时,就有 Circle / Rectangle 两个类,它们都是 Shape 的子类,而且它们都实现了自己的 draw 方法,而这种行为就是重写(重新实现 draw 方法);

注:When a draw message is sent to a circle object, the method dispatcher runs the overridden method—Circle’s implementation of draw. Any implementation of draw defined by a superclass, such as Shape, is completely ignored.(由于调度优先级的存在,调度会先从子类开始到根类,而子类一旦有相应的消息方法,那么就会直接调度而不会再进行深一层的查找(继承链),会直接忽略父类的相同方法)

???问题:假设现在要把所有创建的圆实例对象的红色填充修改为绿色填充?

第一种就是,每一个实例对象都调用 [ Circle setFillColor:kGreenColor ];直接进行设置(实际上是调用了父类的颜色填充方法,因为父类的颜色填充方法没有颜色判断功能,只是单纯的颜色填充,所以导致每一个实例对象都要自己去设置颜色,而且 Circlr 还无法保证设置是否符合要求);

第二种就是, Circle 类自己写一个设置颜色的方法,只要不是绿色的都改成绿色,再进行颜色填充;(重写 父类方法)

看代码:


代码分析:
"super setFillColor":这句代码就是使用父类的填充颜色方法;当然自己重新写也可以;

重写的方法(setFillColor:)的调度过程:

注:如果重写了父类的方法,建议还是调用 [ super setFillColor:c ];这样可以保证父类做完它应该做的事,避免不必要的错误。

上一篇下一篇

猜你喜欢

热点阅读