类的本质-类对象

前言

今天整理了下本身电脑里的一些碎片笔记,时间有限只整理了这篇文章——类的本质,你们能够进行参考。编程

1.本质

  • 类的本质其实也是一个对象(类对象)
  • 程序中第一次使用该类的时候被建立,在整个程序中只有一份。
  • 此后每次使用都是这个类对象,它在程序运行时一直存在。
  • 类对象是一种数据结构,存储类的基本信息:类大小,类名称,类的版本,继承层次,以及消息与函数的映射表等
  • 类对象表明类,Class类型,对象方法属于类对象
  • 若是消息的接收者是类名,则类名表明类对象
  • 全部类的实例都由类对象生成,类对象会把实例的isa的值修改为本身的地址,每一个实例的isa都指向该实例的类对象

2.如何获取类对象

  • 经过实例对象数组

    格式:[实例对象 class]; 如: [dog class];
  • 经过类名获取(类名其实就是类对象)安全

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

3.类对象的用法

  • 用来调用类方法
[Dog test]; Class c = [Dog class]; [c test];
  • 用来建立实例对象
Dog *g = [Dog new]; Class c = [Dog class]; Dog *g1 = [c new];

4.类对象的存储


存储.png

5.OC实例对象、类对象、元数据、之间关系

  • Objective-C是一门面向对象的编程语言。ruby

    • 每个对象 都是一个类的实例。
    • 每个对象 都有一个名为isa的指针,指向该对象的类。
    • 每个类都描述了一系列它的实例的特色,包括成员变量的列表,成员函数的列表等。
    • 每个对象均可以接受消息,而对象可以接收的消息列表是保存在它所对应的类中。
  • 在XCode中按Shift + Command + O打开文件搜索框,而后输入NSObject.h和objc.h,能够打开 NSObject的定义头文件,经过头文件咱们能够看到,NSObject就是一个包含isa指针的结构体,以下图所示:
NSObject.h @interface NSObject <NSObject> { Class isa OBJC_ISA_AVAILABILITY; }
objc.h /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; /// Represents an instance of a class. struct objc_object { Class isa OBJC_ISA_AVAILABILITY; };
  • 按照面向对象语言的设计原则,全部事物都应该是对象(严格来讲 Objective-C并无彻底作到这一点,由于它有象int,double这样的简单 变量类型)
    • 在Objective-C语言中,每个类实际上也是一个对象。每个类也有一个名为isa的指针。每个类均可以接受消息,例如[NSObject new],就是向NSObject这个类发送名为new的消息。
    • 在XCode中按Shift + Command + O,而后输入runtime.h,能够打开Class的定义头文件,经过头文件咱们能够看到,Class也是一个包含isa指针的结构体,以下图所示。(图中除了isa外还有其它成员变量,但那是为了兼容非2.0版的Objective-C的遗留逻辑,你们能够忽略它。)
runtime.h struct objc_class { Class isa OBJC_ISA_AVAILABILITY; #if !__OBJC2__ Class super_class OBJC2_UNAVAILABLE; const char *name OBJC2_UNAVAILABLE; long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; long instance_size OBJC2_UNAVAILABLE; struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; struct objc_method_list **methodLists OBJC2_UNAVAILABLE; struct objc_cache *cache OBJC2_UNAVAILABLE; struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE;
  • 由于类也是一个对象,那它也必须是另外一个类的实例,这个类就是元类 (metaclass)。数据结构

    • 元类保存了类方法的列表。当一个类方法被调用时,元类会首先查找它自己是否有该类方法的实现,若是没有则该元类会向它的父类查找该方法,直到一直找到继承链的头。
    • 元类(metaclass)也是一个对象,那么元类的isa指针又指向哪里呢?为了设计上的完整,全部的元类的isa指针都会指向一个根元类(root metaclass)。
    • 根元类(root metaclass)自己的isa指针指向本身,这样就行成了一个闭环。上面说􏰀到,一个对象可以接收的消息列表是保存在它所对应的类中的。在实际编程中,咱们几乎不会遇到向元类发消息的状况,那它的isa 指针在实际上不多用到。不过这么设计保证了面向对象的干净,即全部事物都是对象,都有isa指针。
    • 因为类方法的定义是保存在元类(metaclass)中,而方法调用的规则是,若是该类没有一个方法的实现,则向它的父类继续查找。因此为了保证父类的类方法能够在子类中能够被调用,因此子类的元类会继承父类的元类,换而言之,类对象和元类对象有着一样的继承关系。
  • 下面这张图或许可以 让你们对isa和继承的关系清楚一些

其中:实线箭头表明类的继承关系,好比EOCStudent继承自EOCPerson,也就是说,EOCStudent是EOCPerson的子类。就能够用实线表示这种继承关系:EOCStudent —>EOCPerson。编程语言

虚线箭头表明对象和类的从属关系,好比一个对象student属于EOCStudent类,也就是说,student是EOCStudent的实例。就能够用虚线表示这种从属关系:student—>EOCStudent。函数

引用《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中的一段话:superclass指针肯定了继承关系,而isa指针描述了实例所属的类。经过这张布局关系图便可进行“类型信息查询”。咱们能查出对象是否可以响应某个选择子(selector),是否听从某项协议,而且可以看出某对象位于集成体系的哪一部分布局


继承/从属关系图
  • 上图中,最让人困惑的莫过于Root Class了。在实现中,Root Class是指
  • NSObject,咱们能够从图中看出:
  • NSObject类对象包括它的对象实例方法。
  • NSObject的元对象包括它的类方法,例如new方法。
  • NSObject的元对象继承自NSObject类。
  • 一个NSObject的类中的方法同时也会被NSObject的子类在查找方法时找到。

6.如何查询类型信息

可使用“类型信息查询方法”来查询类的继承体系。其中,“isMemberOfClass:”能够判断对象是不是特定类的实例。而”isKindOfClass:”能够判断对象是不是某个类或者其派生子类的实例。而本质上,这两个类型信息查询方法是使用对象的isa指针获取对象所属的类(由于类对象也是对象,因此也有isa指针,该指针指向元类,也就是类对象所属的类),而后经过类继承体系中的superclass指针在继承体系中游走。Objective-C与其余语言不一样,Objective-C必须查询类型信息,才能彻底了解对象的真实类型。ui

另外,须要注意的是,咱们从集合对象(collection)中获取的对象,一般会用到这两个查询类型信息的方法。由于从集合对象中取出来的对象不是强类型的(strongly typed),其类型一般是id。回想一下,咱们从一个数组中取出来的对象,其返回值是id类型的。这就是为何咱们能够在这个取出来的对象身上经过中括号”[ ]”的形式调用任何方法,却不能经过点语法来调用方法。不过,为了安全起见,若是涉及到对集合对象中的某个对象进行操做,咱们仍是须要作一下类型判断比较好。以下所示:spa

for (id object in array) { if (object isKindOfClass:[NSString class]) { // object is an instance of NSString } }

固然,也能够用比较类对象是否等同的方法来判断对象是否属于某个类。如果如此,那就应该使用==操做符,而不要使用比较Objective-C对象使经常使用的“isEqual:”方法。由于==操做符比较的是指针是否相等,也就是比较内存地址是否相同。而"isEqual:"比较的是两个Objective-C对象的值是否相等。此处用==操做符,缘由在于,类对象类对象是“单例”,在应用程序范围内,每一个类的Class仅有一个实例,在整个内存中仅有一份(由于+(void)load方法和+ (void)initialize只被调用一次)。因此也能够用下面这种方进行比较:

if ([object class] == [EOCSomeClass class]) { // object is an instance of EOCSomeClass }

虽然调用class方法和isKindOfClass:方法均可以查询一个对象的类型。可是仍是建议使用后者。下面笔者引用《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》中的一段话来进行解释:

虽然使用"class方法"也能够查询对象的类型信息。可是仍是建议使用isKindOfClass:这样的类型信息查询方法。由于后者能够正确处理那些使用了消息传递机制对象。比方说某个对象可能会把其的全部选择子(selector)都转发给另外一个对象(开启了消息转发功能)。这样的对象叫作”代理(proxy)“,此种对象所属的类均以NSProxy为根类(root class)。一般状况下,若是在此种代理对象上调用class方法,那么返回的是代理对象自己(NSProxy的子类),而非接受代理的对象所属的类。然而,如果改用“isKindOfClass:”这样的类型信息查询方法,那么代理对象就会把这条消息转给“接受代理的对象(proxy object)”。也就是说,这条消息(指isKindOfClass:)的返回值与直接接受代理的对象身上查询其类型信息所得的结果相同。所以,这样查出来的类对象与直接经过class方法所返回的那个类对象不一样,class方法所返回类表示发起代理的对象,而非接受代理的对象

文/VV木公子(简书做者)
PS:如非特别说明,全部文章均为原创做品,著做权归做者全部,转载转载请联系做者得到受权,并注明出处,全部打赏均归本人全部!

若是您是iOS开发者,或者对本篇文章感兴趣,请关注本人,后续会更新更多相关文章!敬请期待!

相关文章
相关标签/搜索