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

2023-10-27

在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类与对象

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

类对象、实例对象、isa指针 的相关文章

  • 什么是运行时环境?

    有人可以用简单的术语解释一下这意味着什么吗 是指应用程序代码运行的环境 DOS Windows Linux 等 吗 将其与开发环境和构建环境区分开来 您往往会在这里找到层次结构 运行时环境 执行程序所需的一切 但没有工具可以更改它 构建环境
  • 如何在运行时替换 Moose 对象的方法?

    是否可以在运行时替换 Moose 对象的方法 通过查看源代码Class MOP Method http search cpan org perldoc Class MOP Method which Moose Meta Method htt
  • PyCharm:如何推断运行时创建的对象的类型

    我正在尝试使用一个创建对象的库 并在运行时将它们添加到全局命名空间 PyCharm 无法找到对对象的引用 因为它们最初不在命名空间中 如何让 PyC harm 内省不抱怨 找不到参考 我不想用noinspection tags 示例代码 为
  • Microsoft Visual C++ 运行时版本?我需要把它们全部保留吗?

    我环顾四周 不确定是否能找到明确的答案 所以如果之前有人问过这个问题 我很抱歉 我的 Google Fu 让我失望了 我们正在针对 NET Framework 开发软件 但从历史上看 我们必须包含许多第三方组件 此外 从历史上看 这些往往与
  • 使用 java 中的参数执行 shell 脚本

    我已经用谷歌搜索了一段时间 每个人似乎都有不同的解决方案 但似乎没有一个对我有用 我都尝试过ProcessBuilder and Runtime 两者都调用 sh直接文件并将其输入 bin bash 没有运气 回到基础 我当前的代码如下 S
  • Objective-C 中的标记指针

    While 回答这个问题 https stackoverflow com a 20362087 115200我注意到现代 Objective C 运行时使用标记指针 这迈克 阿什 Mike Ash 的文章及其评论 http www mike
  • 创建“拼写检查”,以合理的运行时间检查数据库

    我不是在询问如何实现拼写检查算法本身 我有一个包含数十万条记录的数据库 我想要做的是针对所有这些记录的表中的特定列检查用户输入 并返回具有特定汉明距离的任何匹配项 同样 这个问题不是关于确定汉明距离等 当然 目的是创建一个 您的意思是 功能
  • constexpr 函数何时在编译时进行求值?

    既然声明为 constexpr 的函数有可能在运行时被调用 那么编译器根据什么标准决定是在编译时还是在运行时计算它呢 template
  • MySQL Workbench 连接到 AWS 上的 RDS 数据库实例时执行的任何操作都会出现极大延迟

    首先 我完全意识到 Stack Overflow 本身可能不是一个理想的子论坛来问这个问题 但我问了我的教授 他告诉我去问 Bing AI 而 Bing AI 告诉我在这里问这个问题 所以如果还有其他一些特定的子论坛会更好 请在评论或答案中
  • 类型稳定性如何让 Julia 如此之快?

    我听说类型稳定性使 Julia 如此之快 同时仍然与其他解释语言 例如 Python 一样具有表达能力 类型稳定性允许编译器在编译时直接根据输入类型确定函数的输出类型 因为 Julia 专门针对每种输入类型进行编译 这意味着如果所有函数都是
  • 无法调试,致命信号 6 (SIGABRT) 位于 0x00007c37(代码=-6)

    我遇到了以下情况 无法调试 Android 应用程序 我可以运行它 但无法调试它 我开始了全新的项目 复制了所有内容并且它有效 意味着能够调试 但又得到了这个 我在模拟器和手机上都尝试过 但没有成功 我使用的是Android Studio
  • 限制 PHP 函数或命令的执行时间[重复]

    这个问题在这里已经有答案了 您好 是否可以仅对命令或函数设置时间限制 例如 function doSomething code here function1 some code here 我只想为 function1 设置时间限制 存在 s
  • 在 Java Runtime.getRuntime().exec(...) 中使用引号和双引号

    我正在尝试在 Mac OSX 中从 Java 启动 Lisp 映像 使用控制台中的图像 我输入以下内容 lisp image eval package method some argument 一切都运行良好 在Java中 我在使用传递引号
  • 流畅的界面是否会显着影响 .NET 应用程序的运行时性能?

    我目前正忙于为现有技术实现一个流畅的接口 这将允许类似于以下代码片段的代码 using var directory Open Directory path to some directory using var file Open File
  • C# DLL 运行时错误 - 构建、运行,但在尝试使用 DLL 函数时抛出未处理的异常

    我正在尝试编译所示的示例项目here http www ftdichip com Support SoftwareExamples FTDIChip ID htm对于 C 它必须转换为 VS2010 但效果很好 它会构建 运行 但当它尝试访
  • 如何在动态创建的一组 editText 上设置 onFocusChangeListener()?

    我有这段代码 每次前一个 lineaLayout 的 edittext 失去焦点时 我都会膨胀一个包含 3 个 editText 的 LinearLayout 我只想在最近创建的 editTexts 上使用 onFocusChangeLis
  • 哪些属性有助于运行时 .Net 性能?

    我正在寻找可用于通过向加载器 JIT 编译器或 ngen 提供提示来确保 Net 应用程序获得最佳运行时性能的属性 例如我们有可调试属性 http msdn microsoft com en us library k2wxda47 aspx
  • 从 datagridview C# 中检索数字值

    我正在尝试从 datagridview 检索数值 表中的值和变量 weeklyTotal 的数据类型都是整数 我也试图将其转换为整数 我浏览了整个网站是否有类似的问题 但没有一个解决方案有帮助 我收到的错误消息是 当转换为数字时 该值必须小
  • 运行时嵌套循环的数量

    我正在尝试输出一组整数从 1 到 max 的所有可能的唯一整数组合 因此 对于 3 个整数且最多 4 个整数 我会得到 123 124 134 234 我正在使用嵌套的 for 循环来执行此操作 但我希望允许用户在运行时输入整数的数量 现在
  • FORTRAN:数据多态

    我试图隐藏真实数据类型和复杂数据类型之间的差异 在 FORTRAN 2003 中 我认为可能有一种方法可以做到这一点 目标是定义一个多态可分配数组 其类型可以在运行时决定 另外 还有一个子例程 它使用多态数组来做一些代数 相同的方程适用于真

随机推荐

  • 深度分析Linux下双网卡绑定七种模式 多网卡的7种bond模式原理

    多网卡的7种bond模式原理 Linux网卡绑定mode共有七种 0 6 bond0 bond1 bond2 bond3 bond4 bond5 bond6 常用的有三种 mode 0 平衡负载模式 有自动备援 但需要 Switch 支援及
  • Android的Button属性使用

    目录 1 文字大小 颜色 2 自定义背景形状 一 Shape的属性 rectangle oval line ring 二 shape的子属性 corners gradient padding size solid stroke 3 自定义按
  • R语言(5) 折线图、散点图

    低级绘图函数 lines abline points 函数lines 其作用是在已有图上加线 命令为lines x y 其功能相当于plot x y type l lines比abline更灵活 函数abline 可以在图上加直线 其使用方
  • 【ESP32-IDF】04-3 存储-FATFS

    主系统flash挂载fatfs文件系统 文章目录 主系统flash挂载fatfs文件系统 1 概述 2 步骤 2 1 修改分区表 2 2 在主系统flash中挂载文件系统 2 3 使用操作文件的函数进行操作 2 4 卸载文件系统 3 总的程
  • Dlib 通过NVIDIA的 CUDA 使用GPU

    使用工具列表 经过各种尝试 只有使用cudnn7这个版本 才能使人脸识别的net函数运行正常 如果有朋友能把其它版本跑正常 可以在下方评论区留言 win10 dlib 19 22 zip VS2019 cudnn 10 2 windows1
  • 操作系统学习8同步互斥问题

    回顾一下同步互斥的概念 现代操作系统基本都是多任务操作系统 即同时有大量可调度实体在运行 在多任务操作系统中 同时运行的多个任务可能 都需要访问 使用同一种资源 多个任务之间有依赖关系 某个任务的运行依赖于另一个任务 这两种情形是多任务编程
  • 稳定性、效率和成本

    互联网企业容器技术实践 第3章美丽联合容器云实践 本章首先介绍美丽联合集团基于Kubernetes和Docker容器云平台的技术方案 架构演进的三个阶段 以及在稳定性 效率和成本三方面所做的工作 然后介绍关键技术方案及创新点 最后谈一下个人
  • 猜数字游戏

    Random random new Random 随机产生数值 int num random nextInt 11 11的意思是取值范围是 1 11 Scanner s new Scanner System in 从控制台输入 int ti
  • Python作业(二)

    作业典例汇总 二 注 已输入的库已全局应用 有需要可自行添加 1 数学计算 难度 在 3 行中分别输入一个浮点数 利用下面公式计算并输出 x 的值 严格保留小数点后2位数字 a 学号最后一位 b 1 c 1 import math a 9
  • opencv 从摄像头中读取视频并保存(c++版)

    opencv中的视频操作函数如下表所列 VideoCapture VideoCapture VideoCapture VideoCapture open VideoCapture isOpened VideoCapture release
  • 如何彻底删除HbuilderX

    想要彻底删除HbuilderX 单单把下载HbuilderX的文件夹删除是无法做到彻底删除的 我们可以使用HbuilderX软件自己提供的删除方法来进行 第一步 找到你所下载的文件夹 第二步 找到reset bat双击运行 第三步 按下回车
  • Canvas 移动缩放点击交互设计

    Canvas 地铁类线路绘制设计过程的记录 最近有个地铁类项目H5的 但是如果是一般的单线 网上随便一个项目都能做 可能还实现的效果会更好 可惜 是双向线 这就有点难度了 领导让两天时间加周六日弄一个效果出来 这就百度了一番 没有找到双向的
  • ModuleNotFoundError: No module named ‘distutils.util‘

    Ubuntu 16 04 上使用 ppa 源安装了 python3 8 之后安装 pip 时 python3 8 get pip py 出现这个问题 解决办法 sudo apt get install python3 8 distutils
  • vscode Downloading package ‘C/C++ language components (Windows)‘ Failed.问题解决

    vscode打开项目后无法使用代码跳转等功能 然后提示一堆错误 Downloading package C C language components Windows Failed Retrying Failed Retrying 下载错误
  • DoTween的部分方法解析

    DoTween的各种使用方法有点多 本人只试用了几种比较常用的 后续项目中如果需要用到更多再补吧 详细内容上官网看http dotween demigiant com documentation php using System Colle
  • mbed开发板STM32L476RG回传上位机数据程序

    用途 测试上位机串口发送的数据是否符合要求 如数据位 奇偶校验 终止位等 实物图 红圈为与上位机连接的串口 供电口 Code 使用mbed在线编译平台 选择设备为STM32L476RG include mbed h include plat
  • 网络安全_密码学实验_非对称加密算法RSA

    网络安全 密码学实验 非对称加密算法RSA 一 实验环境 二 非对称加密RSA 1 理解RSA算法原理 2 加密过程 解密过程 一 实验环境 PyCharm 2019 2 4 Professional Edition Python 3 7
  • 关于logisticregression.fit()报错str‘ object has no attribute ‘decode‘的解决

    先上图 这是听的课里的运行代码之后的效果图 然后代码也是源码 但是在我的机器上一直报错 报错提示 str object has no attribute decode 错误的位置是在logisticregression fit 这里 我上网
  • c++多线程中的利器-thread_local

    我们在这里简单介绍一下在c 11中新添加的 thread local 我们在介绍thread local之间首先需要说明一下什么是线程周期 以及什么情况下的变量可以被声明为thread local相等与将一个可变数据 一 什么是thread
  • 类对象、实例对象、isa指针

    文章目录 实例对象 类对象 Class 元类对象 Metaclass 关于isa指针 isa概述 实例对象的isa指针指向类对象 类对象的isa指针指向元类 元类对象的isa指向根元类 举例 推荐阅读 在iOS开发中可能会遇到这样的问题 什