Objective-C 编程语言(2) 类,对象,消息 --- 类(3)类对象

转载请标明出处:blog.csdn.net/zhangxingping

    类对象

    类的定义中包含有各种各样的信息,其中大多数都是关于类的实例的:
    ● 类的名称及其超类的名称
    ● 用于描述实例的一组变量的模版
    ● 方法名称,返回值及其参数
    ● 方法的实现
    这些信息被编译器重新编码成在运行时系统可以识别的数据结构。编译器只用创建一个对象:类对象,来代表这个类。类对象可以访问类的所有信息。这就意味着类对象可以知道类的实例的样子。因此,类对象可以根据类的定义来生成新的类的实例。
    尽管类对象保有类的实例的原型,但他自身并不是类的实例。他自己没有实例变量,也不能执行类的实例的方法。然而,类的定义中可以包含针对类对象的方法———类方法。类方法与实例方法是相对的。类对象同实例一样也会从其超类中继承方法。
    在源代码中,类对象是由其对应的类名来代表的。在下面的示例程序中,Rectangle类使用其从NSObject类中继承而来的类方法version来返回Rectangle类实现的版本号:
    int versionNumber = [Rectangle version];
    只有作为消息表达式中的接收者的时候,类名称才代表类对象。其他情况下,我们都应该向实例发送class消息以获取其id。实例和类对象都可以响应class方法:
    id aClass = [anObject class];
    id rectClass = [Rectangle class];
    正如上面的示例程序中那样,类对象和其他的对象一样可以被转为id类型。但是类对象还可以被更加明确地转换成Class 的类型:
    Class aClass = [anObject class];
    Class rectClass = [Rectangle class];
    所有的类对象都是一种Class。这种使用Class的方式相当于是使用Class类对实例进行静态的类型转换。
    类对象可以被动态地进行类型转换,接收消息以及从别的类来继承方法,因此类对象也是很完善的。他们的独特之处就在于他们是由编译器创建的,自身没有数据结构(实例变量)。在运行时由他们负责创建类的实例。
    
    注意:编译器还会为每一个类创建一个元类对象。正如类对象是用来描述类的实例的,元类对象是用来描述类对象的。我们可以向类对象和类实例发送消息,而元类对象则是运行时系统内部使用的。   
    创建实例
    类对象的责任就是创建新的实例。下面的代码告诉Rectangle类来创建一个新的Rectangle实例,并把它赋值给myRectangle变量。
    id myRectangle;
    myRectangle =[Rectangle alloc];
    其中的alloc方法动态地为新的对象分配内存空间并用0来对其进行初始化。也就是说除了isa变量用来把该实例和对应的类关联起来,其他数据的值都是0。为了使对象更加有意义,通常都需要进行更加完善的初始化操作。这个方法就是init。初始化通常是紧随着空间分配而进行的:
    myRectangle = [[Rectangle alloc] init];
    在向对象发送诸如本章前面示例代码中的那些消息之前,这样或者类似于这样的代码通常都是很有必要的。其中的alloc返回的是新的实例,init方法用来对该实例进行初始化。每个类对象都至少含有一个像alloc这样的方法来产生新的实例;每个实例至少含有一个像init这样的方法来对该实例进行初始化,以便后续使用。初始化方法通常都需要参数来传入特定的值,并且会使用关键字来对这些参数做标记(例如,initWithPosition:size:就是一种可用于对Rectnagle类的实例进行初始化的方法),但是每一个进行初始化操作的方法都应该以"init"开头。

    定制类对象
    在Objective-C中,类也是被当作对象来处理的。这点并不是Objective-C别出心裁,而是有意识的出于设计的考虑。有时候我们可能需要用类来对一个对象进行初始化,而这个类是完全开放的。例如,在应用程序开发包中(Appkit),NSMatrix对象是可以针对某种特定的NSCell对象来进行定制的。
    一个NSMatrix对象承担了创建每个单元格中对象的责任。这有可能发生在对该矩阵进行初始化的时候,也有可能发生在需要增加新的单元格的时候。这个由NSMatrix对象绘制在屏幕上的矩阵在运行时可能根据用户的动作不同而增大,或者变小。当矩阵增大的时候,就需要知道需要创建什么样的新对象来对新增加行或列中的单元格进行填充。
    但是这些新对象应该是什么类型的呢?单元格的种类有很多种。图3-1中的继承关系图展示了Appkit中提供的部分单元格种类:
    
                   NSObject
                     │
                     │
       ┌──────────NSCell──────────────────────────────┐
       │                                              │
   NSBrowserCell                                NSActionCell
                       ┌──────────────┬───────────────┼───────────────────┐
                       │              │               │                   │
                   NSButtonCell  NSTextFieldCell    NSSliderCell      NSFormCell
                       │
                       │
                   NSMenuCell


    当矩阵创建NSCell单元格的时候,这些单元格应该是NSButtonCell对象以便显示开关或者按钮,还是NSTextFieldCell对象以便用户可以键入并编辑这些字段,或者应该是别的类型的NSCell呢?此时,NSMatrix对象应该允许创建任意类型的单元格,甚至是一些类型没有被发明出来的单元格。
    解决这个问题的一个方法就是把NSMatrix类定义成抽象类,并要求使用该类的所有人员都要声明其派生类并实现产生新单元格的方法。由于创建新单元格的方式是由用户实现的,因此可以确保创建的单元格的类型是所需的类型。
    但是这个解决方法需要NSMatrix类的用户来完成本应该由NSMatrix类完成的工作。同时,由于应用程序中可能需要多种类型的矩阵而不是一种,每当创建一种新类型的单元格(NSCell)就需要定义一种新类型的矩阵(NSMatrix),因此这样做就会导致派生类的激增。这样以来, NSMatrix的派生类就会变得很乱。更有甚者,不同项目的程序员可能都需要编写这样的代码来完成同样的工作,以弥补NSMatrix类的不足。
    一个更好的解决方法,也就是NSMatrix类实际采用的方法就是允许使用某种NSCell类对NSMatrix类的实例进行初始化,也就是使用类对象来进行初始化。NSMatrix类中定义了方法setCellClass:来传入这种NSCell类型的类对象,NSMatrix使用这个类对象来对空单元进行填充:
    [myMatrix setCellClass:[NSButtonCell class]];
    其中的NSMatrix类的对象就可以在因矩阵自动而创建新单元的时候使用类对象对其进行初始化。此时,类被当作是对象,被传入到消息中,并可以被赋值给变量。否则,实现这种类型的初始化将是非常困难的。
    
    变量与类对象
    在定义类的时候,我们可以明确指出那些变量是实例变量。该类的每一个实例都有一份这样的我们所声明的实例变量,也就是说每一个对象控制着自己的这些实例变量。然而,在Objective-C中没有与之相对应的类变量。只有可以在类的声明中可以进行初始化的内部数据结构,这些数据结构可以供类使用。另外,类对象是没有权限访问实例变量的;也就是说类对象是不能对其进行初始化,读取之或者修改之的。
    为了能够实现所有类的实例都共享某些数据,就必须使用外部变量。最简单的方式就是在类的实现文件中声明变量,如下:
    int MCLSGlobalVariable;
    @implementation MyClass
    //其他的实现

        更好的一个实现方式就是声明static变量,并提供类方法来对其进行管理。static变量的作用范围只局限于该类中,也就是只局限于实现该类的那个文件中。(与实例变了不同的是,static变量不能被继承,也不能由派生类直接访问和处理。)这种模式通常被用来定义来的共享实例。(更多关于这样的单件模式,请参阅《Cocoa编程基础指南》的“创建单件实例”相关内容)。   

    static MyClass * MCLSSharedInstance;    
    #impementation MyClass
    +(MyClass*) sharedInstance
    {            
        //检查该实例是否存在
        //如果不存在则创建之
         return MCLSSharedInstance;    
    }   
    //类的其它实现

        静态变量是的类对象的功能不再局限于仅仅是用来产生类对象。类对象可以被用来协调它所创建的实例,从已经创建的实例列表中分发实例,或者是管理程序中其他重要的进程。当我们确实只需要该类的一个实例的时候,我们可以把该对象的所有状态都集中在静态变量中,然后使用对应的类方法。这样就节省了分配并初始化实例的过程。    注意:除了上面说的,我们是可以使用外部的非static的变量的。但是这种static的变量限定了其作用域,这样能更好地体现把数据和对象进行封装的目的。    


    初始化类对象
    除了使用来对象来产生实例意外,我们有可能还需要使用类对象来做别的事情,此时我们就有可能需要对类对象进行初始化,就像对实例进行初始化一样。尽管类对象并不是由我们在程序中创建的,但是Objective-C还是提供了一个对其进行初始化的方法:initialize。    如果一个类中使用了静态或者全局变量,那么initialize方法是一个极好的对这些变量进行初始化的地方。例如,假设一个类中需要维护一些实例构成的数组,在initialize方法中我们就可以产生这个数组,甚至是生成一两个缺省的实例,这样数据就OK了,方便后续使用。    运行时系统会在类对象接收任何消息之前向其发送initialize消息。这种先后关系确保了类在被使用之前其运行时环境已经被搭建好了。如果确实不需要进行初始化操作,那么我们就不必编写initialize方法来响应这个消息。    由于继承关系的缘故,如果向一个没有实现initialize方法的类发送initialize方法,那么这个消息会被发回到其超类,尽管这个超类已经接收过initialize消息了。例如,假设类A实现了initialize方法,类B继承与类A但是没有实现initialize方法,那么在类B接收到其他消息之前,运行时会发送initialize消息给B。但是由于B类没有实现initalize方法,那么就会执行类A的initialize方法。因此,类A应该确保其初始化逻辑针对相应的类之被执行一次。    为了避免初始化的逻辑被多次执行,通常使用列表1-3中的模版来实现initialize方法:
    +(void) initialize
    {
        if ( self ==[ThisClass class))
        {
            //进行初始化操作
        }    
    }

    注意:请记住运行时系统会向每一个类都发送initialize消息的。因此在派生类的initialize方法中没有必要向其超类再次发送initialize消息。

    

    根类的方法
    所有的对象,类以及实例都是一样的,都需要和运行时有交互接口。类对象和实例都应该能够对自身的能力进行“反省”,并能报告自己在类继承关系图中的位置。这个责任有NSObject类来承担。
    因此NSObject类中的方法没有必要被两次实现:一次为了实例能有和运行时交互的能力,一次是为了类对象能有和运行时交互的能力。类对象被给与了特殊的待遇:能调用根类中定义的实例方法。当一个类对象接收到一个消息,而自身没有对应的类方法的时候,运行时会查看是否有对应的根类的实例方法。类对象能执行的实例方法是那些在根类中定义的实例方法,并且要求这些实例方法没有对应的类方法。
    更多关于类对象的这种能执行根类的实例方法的特殊能力的信息,请参阅“NSObject类参考”。




                
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值