热门搜索 :
考研考公
您的当前位置:首页正文

Objective-C 中的对象 & isa &

来源:东饰资讯网

说到对象,什么是对象?

在面向对象编程中,有两个重要的概念:对象。在实际的内存中 又是以什么样的形式存在的呢?万物皆屌丝,不对、是万物皆对象。

一、对象

在 OC 中什么是对象?有很长一段时间坚信认为 +alloc 出来的才是对象。如果说平时这么说还行,一旦放到面试的时候这样来回答、那恐怕就不行,因为这个答案是不全的。主要分为以下三种:

  • 1、instance 对象,也称实例对象。
  • 2、class 对象,也称类对象。
  • 3、meta-class 对象,也称元类对象。

没错,就是这三个对象。突然感觉哪里不对劲,不是说好的 block 也是一种特殊的对象么?对,也没有错,但是今天暂时不讨论这个 block 对象。
接下来、将会讨论这些对象中都包含了什么样的信息,这些信息都是如何关联起来的,成员变量是存在哪里的,类方法与实例方法是存在哪里的,如何找到 superclass 的。。。。。关于这些问题,将会一一的梳理一遍。

1.1 instance 对象

1.1.1 什么是 instance 对象

总而言之就是通过 +alloc 之后的的都是 instance 对象。但是这里还需要强调一点的是: instance 对象并不仅仅是 NSObject 的对象,还有一个代理类 NSProxy,也是能创建 instance 对象。

关于 NSProxy,大家应该高度重视,当面试官问你在 OC 中有什么代理类的时候,别说不知道什么代理类,就知道代理协议(delegate)。如果你说不知道,那有一点可以很肯定,YYKit 那么优秀的框架都不去学习一下,对于大厂来说,恐怕会遭到鄙视的。具体的可以参考 YYKit 中的 YYWeakProxy。看完 YYWeakProxy 之后,你还会学到另一个技能:如何处理 OC 中定时器的循环引用。这两个问题在面试中,含金量都不低。在接下来的介绍中,不再提及 NSProxy 类。

代码中是如何获取一个对象的:

// 创建一个对象
NSObject* obj = [NSObject alloc];

1.1.2 instance 对象中的信息

1.2 class 对象

1.2.1 获取 class 对象

class 对象是怎么被创建的?毕竟在开发过程中也没有见过通过 +alloc 的方式创建之。但是知道怎么去获取一个 class 对象,见下面的代码:

{
    // 创建对象
    ClsObject* cObj = [[ClsObject alloc] init];
    
    // 获取 class 的 所有方法
    [self fetchClassWiothCObj:cObj];
}

// 获取 class 的 所有方法
- (void)fetchClassWiothCObj:(ClsObject*)cObj {
    Class obcCls1 = [cObj class];
    Class obcCls2 = [ClsObject class];
    Class obcCls3 = object_getClass(cObj);
    
    NSLog(@"%@, %@, %@", NSStringFromClass(obcCls1), NSStringFromClass(obcCls2), NSStringFromClass(obcCls3));
    // 打印结果: ClsObject, ClsObject, ClsObject
    
    NSLog(@"%p, %p, %p", obcCls1, obcCls2, obcCls3);
    // 打印结果: 0x10f52dd30, 0x10f52dd30, 0x10f52dd30
}

以上代码中的 ClsObject 是一个直接继承于 NSObject 的 Class。

没错,不管是通过什么方法获取的 class 对象都是一样的,包括地址。说明在一个项目中一个 Class 仅有一个对象。但是上面的三种获取 class 对象的方式有什么不一样呢?
第一种与第二种是通过方法获取的,直接获取的是当前 instance 的 Class,但是第三种方式不一样,这种方式是获取当前 instance 的 isa 的值。

可以这样做一个实验,给上面的 cObj 做一个 KVO 监听,我们再看一下打印结果,会发现打印的结果变成了这样的:

// 打印结果:ClsObject, ClsObject, NSKVONotifying_ClsObject
// 打印结果:0x102e5ee10, 0x102e5ee10, 0x60000011a820

是的,第三个值变了,变成了 NSKVONotifying_ClsObject。同时还发现,所有同一个 Class 的 instance 注册的 KVO 的 NSKVONotifying_ 的 class 对象的值也是一样的。

1.2.2 class 对象中的信息

  • 1、isa
  • 2、superclass
  • 3、属性 property
  • 4、instance 方法
  • 5、协议 protocal
  • 6、成员变量,这里的成员变量信息并不是一个 instance 中成员变量的值,而是指在这个 Class 中有哪些成员变量,是 NSSting 的,还是 int 类型的。
  • 7、其它
    。。。。。。。

1.3 meta-class 对象

1.3.1 获取 meta-class 对象

同理在开发中是不会手动去 +alloc 一个元类对象,可以通过 object_getClass 函数获取 class 对象的 isa 类获取之。代码如下:

// 获取元类对象
- (void)metaClass:(ClsObject*)cObj {
    // 获取一个对象的 isa
    Class obcISA = object_getClass(cObj);
    // 获取元类对象
    Class metaClass = object_getClass(obcISA);
    NSLog(@"%p, %@", metaClass, NSStringFromClass(metaClass));
}

会发现,元类还是当前的 Class,但是是另一个对象地址。
其次,不管是 class 对象还是元类对象,其类型都是 Class,说明在内存结构上是一致的。但是其包含的信息含义是不一样,其用途也不一样。

1.3.2 meta-class 对象中的信息

  • 1、isa
  • 2、superclass
  • 3、类方法信息
  • 4、其它

1.4 对象总结

  • 1、总共有三种对象:instance 对象、class 对象与 meta-class 对象
  • 2、成员变量的值都存于 instance 对象中。
  • 3、属性、instance (实例)方法、协议 protocol、成员变量都存于 class 对象中。
  • 4、类方法都存于 meta-class 对象中。
对象的信息分布

二、关于 isa

以上的三种对象是如何关联起来的呢?是通过 isa 关联的:

instance 对象的 isa 的值是 class 对象,class 对象的 isa 的值是 meta-class对象。

尽然实例方法是存在 class 对象中,那么当给一个 instance 对象发送消息的时候,是如何找到具体的方法实现的呢?

当调用实例方法的时候, 通过 instance 对象中的 isa 找到 class,找到对应的实例方法的实现。

同理,类方法的调用也是一样:

当调用类方法的时候,通过 class 对象的 isa 指针找到 meta-class,并找到对应的方法实现。

不管是调用 Class 方法还是对象方法都是消息发送,这里有一个面试题是这样问的:OC 中的消息发送的本质是什么?在之前我是这样的回答的:通过 SEL 去找对应的 IMP 实现,首先是从当前 Class(meta-class) 寻找,如果一旦找不到就会到父类寻找,当所有的都没有找到那么会启动消息转发机制,一旦找到了、那么会将当前的 SEL 与 IMP 缓存起来方便下一次查询。之前一直以为这样的回答够完美的了,但是现在看来需要再加一点专业术语会更加的完善。消息转发的本质是通过 isa 查找对应的 IMP 实现。然后加上之前的回答即可。
为什么要强调这一点呢?难道在 OC 中还有不需要 isa 直接发送消息的??是的、有一个方法被调用就没有通过 isa 的查询,那就是 +load 方法。在很久之前也一直有一个疑问:为什么在分类中重写了 +load 方法之后,原生 Class 的+load 方法还能被调用。原来是因为 +load 方法的调用逻辑是在 dyld 加载阶段,一旦检测到当前的 Class 或者其分类重写了 +load 直接通过 IMP 地址进行调用。所以这种情况就不会出现原生 Class 的 IMP 后移从而导致没有机会被调用的情况。

三、关于 superclass

superclass 指针 是相对于 class 对象meta-class 对象 来说的。这个指针有什么作用呢?
定义两个 Class:Person 继承于 NSObject,Student 继承于 Person。现在有一个场景,通过 Student 的 instance 对象调用 Person 中实现的实例方法,具体的调用过程如下:

通过 Student 类的 instance 对象 的 isa 找到对应 Student 类的 class 对象,但是没有找到相关的实现,系统会继续到 superclass 中找,于是会到 Person 类的 class 对象 中找到具体的实现,并调用。

类方法的调用,也是一样。

四、 isa 与 superclass

美图欣赏,以上所说的都是为了能看懂这张图片:


class.png

由图可知:

1、isa

  • 1、instance 的 isa 指向 class
  • 2、class 的 isa 指向 meta-class
  • 3、meta-class 的 isa 指向基类的 meta-class

2、superclass

  • 1、class 的 superclass 指向父类的 class,如果没有父类,superclass 为 nil
  • 2、neta-class 的 superclass 指向父类的 meta-class,基类的 meta-class 的 super 指向基类的 class

3、 方法调用轨迹

instance 对象: isa 找到class,方法如果不存在,就通过 superclass找父类。
class 对象: isa 找到meta-class,方法如果不存在,就通过 superclass 找父类。

谢谢!

Top