类对象、实例对象、isa指针

在iOS开发中可能会遇到这样的问题,什么是类对象,它和实例对象有什么区别?

实例对象

首先我们来看看经常使用到的实例对象。

什么是实例?站在面向对象的角度上说,实例是一个抽象类具体的某个对象。

由一个类实例化来的对象叫实例对象。

创建一个Person的对象p

Person *p = [[Person alloc] init];

这个p就是一个实例,也叫做实例对象。

在Runtime中对类的实例做如下定义:

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

从定义里我们可以看到一个实例只有一个isa指针,也就是说实例中只存储了一个isa指针地址。

那么是怎么能通过实例调用到具体类的方法和属性呢?

表示实例对象的结构体objc_object里面有一个Class类型的isa指针,这个指针指向实例所属的类对象,类对象里存储了方法、变量等。通过isa指针就可以找到类对象中的方法了。

在这里插入图片描述
图片出处

类对象(Class)

实例对象所属的类,称为类对象。

引用别人的话来总结:“在Objective-C中,对象是广义的概念,类也是对象,所以严谨的说法应该是类对象和实例对象。既然实例对象所属的类称为类对象,那类对象有所属的类吗?有,称之为元类(Metaclass)”。

出处

Runtime对类对象的定义如下:

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;

objc_class结构体如下

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */

类对象中存储的数据即这个结构体中的数据

isa指针,指向类对象所属的类,即原类。原类中存储着类对象的类方法,当访问某个类的类方法时会通过该isa指针从元类中寻找方法对应的函数指针。

super_class,指向父类的指针,如果该类已经是最顶层的根类(如NSObject或NSProxy), 则 super_class为NULL。

其他的方法名,大小,方法链表,方法缓存链表,协议链表等信息。

元类对象(Metaclass)

“元类就是类对象的类,每个类都有自己的元类,也就是objc_class结构体里面isa指针所指向的类. Objective-C的类方法是使用元类的根本原因,因为其中存储着对应的类对象调用的方法即类方法。“

OC中调用方法,也就是向对象发送消息

  • 当向实例对象发送消息时,通过对象的isa指针在对象所属的类中查找方法以及实现。

  • 当向类对象发送消息时,通过类对象的isa指针在其元类中寻找方法以及实现。

第一种就是我们常说的调用实例方法的实现过程,第二个就是调用类方法的实现过程。

runtime中可以直接使用以下函数获取元类对象

//获取元类
Class getNSObjectMetaClass = objc_getMetaClass("NSObject");

这个函数在runtime.h中定义的,需要引入头文件然后直接使用。

objc_getMetaClass在runtime中的实现

Class objc_getMetaClass(const char *aClassName)
{
    Class cls;

    if (!aClassName) return Nil;

    cls = objc_getClass (aClassName);
    if (!cls)
    {
        _objc_inform ("class `%s' not linked into application", aClassName);
        return Nil;
    }

    return cls->ISA();
}

看这个函数的实现有助于我们理解isa指针的指向、元类、类之间的关系。

关于isa指针

isa概述

实例对象的isa指针

/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

类对象的isa指针

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

...

实例对象的isa–> 实例所属的类对象

类对象的isa–> 类的元类对象

实例对象的isa指针指向类对象

以Person为例,有一个实例p

// 返回Person类对象的本身的地址
Class class0 = [Person class];
// isa指向的Person类对象的地址
Class class1 = [p class];
// isa指向的Person类对象的地址
Class class2 = object_getClass(p);

NSLog(@"class0 -> %p", class0); // -> 0x10a2157d0
NSLog(@"class1 -> %p", class1); // -> 0x10a2157d0
NSLog(@"class2 -> %p", class2); // -> 0x10a2157d0

针对class0有一个问题:
Person是个类对象,它的isa指针不是应该指向元类对象吗,怎么这里打印的地址是自己的地址?

类对象的isa指针指向元类

// 返回Person类对象本身的地址
Class class3 = [class0 class];
// isa指向的Person类对象的元类
Class class4 = object_getClass(class0);
// isa指向Person类对象的元类
Class class5 = object_getClass(class1);

NSLog(@"class3 -> %p", class3); // -> 0x10a2157d0
NSLog(@"class4 -> %p", class4); // -> 0x10a2157f8
NSLog(@"class5 -> %p", class5); // -> 0x10a2157f8

class4、class5打印的就是元类的地址。

元类对象的isa指向根元类

略…

举例

通过打印地址,更好的理解 类对象、元类对象、实例对象、isa指针以及它们之间的关系。

//NSObject的类对象
Class class_NSObject = [NSObject class];
//NSObject类对象的元类对象
Class metaClass_NSObject = object_getClass(class_NSObject);
//NSObject类对象的元类的元类对象,即根元类对象
Class rootMetaClass_NSObject = object_getClass(metaClass_NSObject);
//NSObject的根元类对象的元类对象
Class root_rootMetaClass_NSObject = object_getClass(rootMetaClass_NSObject);
//这里直接用函数获取元类
Class getNSObjectMetaClass = objc_getMetaClass("NSObject");

//NSObject的类对象地址
NSLog(@"class_NSObject -> %p",class_NSObject);                          // -> 0x1104b8ea8
//NSObject的类对象的元类对象地址
NSLog(@"metaClass_NSObject -> %p",metaClass_NSObject);                  // -> 0x1104b8e58
//NSObject的类对象的元类对象的元类地址
NSLog(@"rootMetaClass_NSObject -> %p",rootMetaClass_NSObject);          // -> 0x1104b8e58
//NSObject的类对象的元类的元类地址
NSLog(@"root_rootMetaClass_NSObject -> %p",root_rootMetaClass_NSObject);// -> 0x1104b8e58
//NSObject的类对象的元类
NSLog(@"getNSObjectMetaClass -> %p",getNSObjectMetaClass);// -> 0x1104b8e58


//Person类对象
Class class_Person = [Person class];
//Person类对象的元类
Class metaClass_Person = object_getClass(class_Person);
//Person类对象的根元类
Class rootMetaClass_Person = object_getClass(metaClass_Person);
//Person类对象的根元类的元类对象
Class root_rootMetaClass_Person = object_getClass(rootMetaClass_Person);

NSLog(@"class_Person -> %p",class_Person);                            // -> 0x10f4d48b8
NSLog(@"metaClass_Person -> %p",metaClass_Person);                    // -> 0x10f4d48e0
NSLog(@"rootMetaClass_Person -> %p",rootMetaClass_Person);            // -> 0x1104b8e58
NSLog(@"root_rootMetaClass_Person -> %p",root_rootMetaClass_Person);  // -> 0x1104b8e58



//对象p所属的类对象,对象p的isa指针指向所属的类
Class pClass = [p class];
//对象p的元类对象,类对象的isa指针指向元类
Class pClass_MetaClass = object_getClass(pClass);
//对象p的元类的元类
Class pClass_MetaClass_MetaClass = object_getClass(pClass_MetaClass);
//对象p的元类的元类的元类
Class pClass_root_rootMetaClass_p = object_getClass(pClass_MetaClass);

//对象p的地址
NSLog(@"p -> %p",p);                                               // -> 0x60800024a710
//对象p的元类地址
NSLog(@"pClass_MetaClass -> %p",pClass_MetaClass);                      // -> 0x10f4d48e0
//对象p的元类的元类地址
NSLog(@"pClass_MetaClass_MetaClass -> %p",pClass_MetaClass_MetaClass);  // -> 0x1104b8e58
//对象p的根元类地址
NSLog(@"pClass_root_rootMetaClass_p -> %p",pClass_root_rootMetaClass_p);// -> 0x1104b8e58
//对象p的根元类的元类地址
NSLog(@"pClass_MetaClass_MetaClass -> %p",pClass_MetaClass_MetaClass);  // -> 0x1104b8e58
  • NSObject的元类的isa指针指向自身,即NSObject的元类就是其根元类

  • 实例对象的isa指针指向其所属的类,类的isa指向其元类

  • 元类的isa指向根元类,根元类的isa指针指向自身

  • 所有继承于NSObject的类的元类的根元类都是同一个类,即NSObject的元类/根元类

在这里插入图片描述
图片出处

推荐阅读

格物致知iOS类与对象

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Morris_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值