码迷,mamicode.com
首页 > 其他好文 > 详细

OC基础--类的本质

时间:2015-09-07 00:16:45      阅读:246      评论:0      收藏:0      [点我收藏+]

标签:

类的本质:

  • 类的本质其实也是一个对象(类对象),只要有了类对象, 将来就可以通过类对象来创建实例对象
  • 程序中第一次使用该类的时候被创建,在整个程序中只有一份。此后每次使用都是这个类对象,它在程序运行时一直存在。
  • 类对象是一种数据结构,存储类的基本信息:类大小,类名称,类的版本,继承层次,以及消息与函数的映射表等
  • 类对象代表类,Class类型,保存了当前对象所有的对象方法,当给一个实例对象发送消息的时候, 会根据实例对象中的isa指针去对应的类对象中查找
  • 如果消息的接收者是类名,则类名代表类对象
  • 所有类的实例都由类对象生成,类对象会把实例的isa的值修改成自己的地址,每个实例的isa都指向该实例的类对象

获取类对象:

  通过实例对象:

    格式:[实例对象   class ];
    如:   [dog class];

  通过类名获取(类名其实就是类对象)

    格式:[类名 class];
    如:[Dog class]

类对象的用法:

  用来调用类方法:

    [Dog test];

    Class c = [Dog class];
    [c test];

  用来创建实例对象:

    Dog *g = [Dog new];

    Class c = [Dog class];
    Dog *g1 = [c new];

类对象的存储:

    技术分享  

OC实例对象 类对象 元对象:

  OC对象:

    1 每一个对象都是一个类的实例。

    2 每一个对象都有一个名为isa的指针,指向该对象的类。

    3 每一个类描述了一系列它的实例的特点,包括成员变量的列表,成员函数的列表等。 

    4 每一个类实际上也是一个对象。每一个类也有一个名为isa的指针。每一个类都可以接受消息,例如[NSObject new],就是向NSObject这个类发送名为new的消息。

  元类:

    因为类也是一个对象,那它也必须是另一个类的实例,这个类就是元类 (metaclass)

    1 元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它本身是否有该类方法的实现,如果没有则该元类会向它的父类查找该方法,直到一直找到继承链的头。

    2 元类(metaclass)也是一个对象,也有isa指针,所有的元类的isa指针都会指向一个根元类(root metaclass)。

    3 根元类(root metaclass)本身的isa指针指向自己,这样就行成了一个闭环。一个对象能够接收的消息列表是保存在它所对应的类中的。在实际编程 中,我们几乎不会遇到向元类发消息的情况,那它的isa 指针在实际上很少用到。不过这么设计保证了面向对象的干净,即所有事物都是对象,都有isa指针。

    4 由于类方法的定义是保存在元类(metaclass)中,而方法调用的规则是,如果该类没有一个方法的实现,则向它的父类继续查找。所以为了保证父类的类方法可以在子类中可以被调用,所以子类的元类会继承父类的元类,换而言之,类对象和元类对象有着同样的继承关系。

      技术分享

      假设: Person类有一个对象方法test 和 一个类方法 demo

      Student类有一个对象方法test1 和一个类方法 demo1

       技术分享

      在实现中,Root Class是指 NSObject

        NSObject类对象包括它的对象实例方法。

        NSObject的元对象包括它的类方法,例如new方法。

        NSObject的元对象继承自NSObject类。

        一个NSObject的类中的方法同时也会被NSObject的子类在查找方法时找到。

类的启动过程:

  +load方法:

    1 在程序启动的时候会加载所有的类和分类,并调用所有类和分类的+load方法(只会调用一次),将所有类的代码加载到内存中, 放到代码区

    2 先加载父类,再加载子类;也就是先调用父类的+load,再调用子类的+load

    3 先加载原始类,再加载分类

    4 不管程序运行过程有没有用到这个类,都会调用+load加载

    @implementation Person
 
    + (void)load
    {
        NSLog(@"%s", __func__);
    }
    @end 

    @implementation Student : Person
 
    + (void)load
    {
        NSLog(@"%s", __func__);
    }
    @end 
    输出结果:
      +[Person load]
      +[Student load]

  +initialize方法:

    1 在第一次使用某个类时(比如创建对象等),会调用一次+initialize方法,  无论使用多少次这个类都只会调用一次

    2 initialize用于对某一个类进行一次性的初始化

    3 一个类只会调用一次+initialize方法,先调用父类的,再调用子类的

    @implementation Person
    + (void)initialize
    {
        NSLog(@"%s", __func__);
    }
    @end 

    @implementation Student : Person
    + (void)initialize
    {
        NSLog(@"%s", __func__);
    }
    @end

    int main(int argc, const char * argv[]) {
        Student *stu = [Student new];
        return 0;
    }
    输出结果:
      +[Person initialize]
      +[Student initialize]

SEL类型:

  概念:

    1 SEL类型代表着方法的签名,在类对象的方法列表中存储着该签名与方法代码的对应关系

    2 每个类的方法列表都存储在类对象中,  每个方法都有一个与之对应的SEL类型的对象,  根据一个SEL对象就可以找到方法的地址,进而调用方法

    3 SEL类型的定义   typedef struct objc_selector *SEL;

  -test方法的调用:

    1 首先把test这个方法名包装成sel类型的数据

    2 根据SEL数据到该类的类对象中,去找对应的方法的代码,如果找到了就执行该代码

    3 如果没有找到根据类对象上的父类的类对象指针,去父类的类对象中查找,如果找到了,则执行父类的代码

    4 如果没有找到,一直像上找,直到基类(NSObject), 如果都没有找到就报错。

    在这个操作过程中有缓存,第一次找的时候是一个一个的找,非常耗性能,之后再用到的时候就直接使用。

  用途:

    1 配合对象/类来检查对象/类中有没有实现某一个方法 

   SEL sel = @selector(setAge:);
    Person *p = [Person new];
    // 判断p对象中有没有实现-号开头的setAge:方法
    // 如果P对象实现了setAge:方法那么就会返回YES
    // 如果P对象没有实现setAge:方法那么就会返回NO
    BOOL flag = [p respondsToSelector:sel];
    NSLog(@"flag = %i", flag);    

    // respondsToSelector注意点: 如果是通过一个对象来调用该方法那么会判断该对象有没有实现-号开头的方法
    SEL sel1 = @selector(test);
    flag = [p respondsToSelector:sel1];
    NSLog(@"flag = %i", flag);    

    // 如果是通过类来调用该方法, 那么会判断该类有没有实现+号开头的方法
    flag = [Person respondsToSelector:sel1];
    NSLog(@"flag = %i", flag);

  2 配合对象/类来调用某一个SEL方法 

   SEL sel = @selector(test);
    Person *p = [Person new];
    // 调用p对象中sel类型对应的方法
    [p performSelector:sel];   

    // withObject: 需要传递的参数
    // 注意: 如果通过performSelector调用有参数的方法, 那么参数必须是对象类型,因为withObject只能传递一个对象
     SEL sel2 = @selector(setAge:);
     [p performSelector:sel2 withObject:@(5)];
     NSLog(@"age = %i", p.age);    

    // 注意:performSelector最多只能传递2个参数
    SEL sel3 = @selector(sendMessageWithNumber:andContent:);
    [p performSelector:sel3 withObject:@"138383438" withObject:@"abcdefg"];

  3 配合对象将SEL类型作为方法的形参   

  // 调用传入对象的指定方法
    - (void)makeObject:(id)obj andSel:(SEL)sel; 

    - (void)makeObject:(id)obj andSel:(SEL)sel
    {
       [obj performSelector:sel];
    } 

    Car *c = [Car new];
    SEL sel = @selector(run);    
    Person *p = [Person new];
    [p makeObject:c andSel:sel];

 

OC基础--类的本质

标签:

原文地址:http://www.cnblogs.com/dx-230/p/4787600.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!