多态概述

   
     多态指同一操作作用于不同的对象。能够有不同的解释。产生不同的执行结果。它是面向对象程序设计(OOP)的一个重要特征,动态类型能使程序直到执行时才确定对象的所属类。其详细引用的对象在执行时才干确定。

动态绑定能使程序直到执行时才确定调用对象的实际方法。

         C++中的多态性详细体如今执行和编译两个方面,编译时多态是静态多态(重载、模版)。在编译时就能够确定对象使用的形式,执行时多态是动态多态(虚函数抽象类,覆盖)。
   
      C++使用虚函数(虚函数表)来实现动态绑定,当基类对象的指针(或引用)指向派生类的对象时候,实际调用的是派生类相应的函数。

         Objective-c
是动态语言,所以它具有动态类型和动态绑定的特性。Objective-c系统总是跟踪对象所属的类。对于类型的推断和方法的确定都是在执行时进行。 那Objective-c是怎么样实现多态特性的呢?
二 Objective-c多态
     首先看以下代码
     draw.h文件

     @interface Draw : NSObject
     @property (nonatomic,strong) NSString *name;
       - (void) Print;
       - (void) draw;
     @end

     
     draw.m文件
      #import "Draw.h"
     @implementation Draw
     @synthesize name;
     - (id) init
     {
         if (self =
[super init])
         {
             self.name = @"Draw Demo";
         }     
         return self;
     }
     - (void) draw
     {
           NSLog(@"Draw::draw.......");
     }
     - (void) Print
     {
         NSLog(@"i am  %@.",self.name);
     }
     @end
     
      cricle.h文件
      #import "Draw.h"
     @interface Circle : Draw
     @end     


      circle.m文件
      #import "Circle.h"

     @implementation Circle
     - (void) draw
     {
         NSLog(@"%@:draw circle",self.name);
     }
     @end

     
     Retangle.h文件
     #import "Draw.h"

     @interface Retangle : Draw
     @end

     
     Retangle.m文件
     #import "Retangle.h"

     @implementation Retangle
     - (void) draw
     {
         [super draw]; //通过superkeyword能够调用基类的draw函数
         NSLog(@"%@:draw retangle",self.name);
     }
     @end

  
    我们定义了一个Draw基类。里面有一个数据成员name,和两个函数成员draw和Print,Circle和Retangle是从Draw派生的两个类,他们重写了基类Draw的draw方法。

代码使用
      
 Draw* base = [[Circle alloc] init];

      [base draw]; //draw circle
      NSLog(@"address:%@",base);
       
      base = [[Retangle alloc] init];
      [base draw]; //draw retangle
      NSLog(@"address:%@",base);
      [base Print];

输出结果

     2014-04-09
15:34:41.648 duotaidemo[7718:303] Draw Demo:draw circle

     2014-04-09 15:34:41.673 duotaidemo[7718:303] address:<Circle: 0x1002027a0>
     2014-04-09 15:34:41.674 duotaidemo[7718:303] Draw::draw.......
     2014-04-09 15:34:41.674 duotaidemo[7718:303] Draw Demo:draw retangle
     2014-04-09 15:34:41.675 duotaidemo[7718:303] address:<Retangle: 0x100205e70>
     2014-04-09 15:34:41.676 duotaidemo[7718:303] i am  Draw Demo.

      使用基类的指针分别指向创建的两个派生类对象,然后分别调用各自的draw函数。通过输出结果能够发现他们调用的是各自的draw方法。

因为Retangele没有重写基类的Print函数,全部使用[base
Print]调用的是基类的方法。

同一时候通过address的输出发现base指向了两个不同的对象。

小结:
        1.与C++ 的多态相比。在Objective-c中是没有virtualkeyword的,默认情况下仅仅要子类重写了父类的方法就实现了覆盖(这一点和java相似),在Objective-c中同一类中的函数是不能被重载的。
        2.在Objective-c中。通过superkeyword能够调用基类的函数,这个在C++中是没有的,在C++中可通过作用域运算符訪问基类成员。

      
     除了上面的调用方式外。我们也能够这样:

    
    id base = [[Circle alloc] init];
        [base draw]; //draw circle
         NSLog(@"address:%@",base);
       
        base = [[Retangle alloc] init];
        [base draw]; //draw retangle
         NSLog(@"address:%@",base);

        [base Print];
   
       其输出结果和上面是一样的
        既然Objective-c中没有像C++一样的虚函数表,那它的多态是怎么实现的?它的类型系统是怎么样构建起来的呢?继续往下看吧!

三  类对象
       尽管Objective-c没有虚函数表,可是它有一个根类NSObject,以下让我们探究一下这个根类是个什么东东。

   
 objc.h 文件里关于NSObject的定义
     @interface NSObject
<NSObject>
     {
         Class isa  OBJC_ISA_AVAILABILITY;

     }

     typedef struct objc_class *Class; 

     typedef struct objc_object
{
         Class isa;

     } *id;
     typedef struct objc_selector  *SEL;
     typedef id (*IMP)(id, SEL,
...);
     详见:http://opensource.apple.com/source/objc4/objc4-493.9/runtime/objc.h
 
通过上面的定义我们能够知道以下事实:
     1.Class isa 是NSObject类的第一个数据成员。
     2.Class 是一个指针,它指向一个objc_class的结构体。

     3.id 类型是一个指针,它指向一个objc_object的结构体,该结构体仅仅有一个成员即Class isa;
     4.id 类型是一个指针,它指向一个存有objc_class的结构对象的指针的指针。

 3.1 isa介绍
以下是苹果官方文档对isa的介绍说明:    
       Every object is connected to the run-time system through
its isa instance variable, inherited from the NSObject class. isa identifies the object's class; it points to a structure that's compiled from the class definition. Through isa, an object can find whatever information it needs at run timesuch as its place
in the inheritance hierarchy, the size and structure of its instance variables, and the location of the method implementations it can perform in response to messages.
              
       实例变量是通过isa成员链接到执行时系统环境中的,随意NSObject的子类都会继承NSObject的isa成员,并且当NSObject的子类实例化对象时,isa实例变量永远是对象的第一个实例变量。isa指向该对象的类对象,它是实例和类对象连接的桥梁。
      
      
     实例变量和类对象的关联,例如以下图所看到的:
IOS - 执行时 (多态)-LMLPHP
以下是类对象(objc_class)的结构体
 
   struct objc_class {
         Class isa;                                             /* metaclass */
Class super_class /* 父类的地址 */
const char *name /* 类名称 */
long version /* 版本号 */
long info /* 类信息 */
long instance_size /* 实例大小 */
struct objc_ivar_list *ivars /* 实例參数列表*/
struct objc_method_list **methodLists /* 方法列表 */
struct objc_cache *cache /* 方法缓存 */
struct objc_protocol_list *protocols /* protocol链表*/
     } ;  
        在Objective-C中类也是一种对象,并且在程序执行时一直存在。

 类对象是一个依据类定义生成的一个结构体,里面存储了类的基本信息,
如:类的大小,类的名称,类的版本号以及消息与函数的映射表等信息。类对象所保存的信息在程序编译时确定,在程序启动 时载入到内存中。

 3.2 id介绍
     
    由上面的定义我们知道,id类型是一个指向类对象的指针的指针。

在Objective-c中,id类型是一种通用的指针类型。id类型能够用来指向属于不论什么类的对象(仅仅要该对象是属于NSObject即成体系)。 

   
    id类型的使用例如以下图所看到的:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvemhhbmd6aGVianV0/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

   在使用id类型的时候要注意:
   
   1. id类型本事是一个指针类型,在使用时就不用加*号了,比如上面的样例 id base
= [[Circle alloc] init];
   2.  id类型是通用指针类型,弱类型,编译时不进行类型检查
       Objective-C能够将对象分为id类型和静态类型。假设不涉及到多态。尽量使用静态类型。
      在上的样例中我们使用了两种方式来调用派生类函数,第一种使用的即使静态类型,另外一种使用的是id动态类型。在写代码时候,尽量使用静态类型,静态类型可更好的在编译阶段而不是执行阶段指出错误,同一时候能够提高程序的可读性。
四 小结
        实例变量中isa成员用于保持其类对象在内存的地址。类对象对于全部实例来说在内存中仅仅有一份副本,不论什么一个实例都能够通过
isa成员,訪问类对象所保持的类的信息,isa成员能够通过类对象获得当前实例能够訪问的消息列表。以及消息相应的函数地址。

         Objecive-c使用类对象的形式来实现执行多态。每一个对象都保存其类对象的地址。类对象中保存了类的基本信息。类对象是进行动态创建(反射),动态识别,消息传递等机制的基础。
      那么上面的程序中,函数的调用过程时怎么样利用类对象的呢?
      下一节将会讲述Objective-c消息传递机制。以进一步解剖函数的调用过程。

05-28 19:55