当前位置: 首页 > news >正文

【iOS】——探究isKindOfClass和isMemberOfClass底层实现

isKindOfClass

判断该对象是否为传入的类或其子类的实例

// 类方法实现,用于检查一个类是否属于另一个类或其父类链上的任何类。
+ (BOOL)isKindOfClass:(Class)cls {// 从当前类开始,tcls将沿着元类的继承链向上遍历。for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {// 检查当前类tcls是否等于要检查的类cls。if (tcls == cls) return YES; // 如果相等,立即返回YES,表示属于该类或其子类。}// 如果遍历完整个继承链都没有找到匹配的类,返回NO。return NO;
}// 实例方法实现,用于检查一个对象是否属于指定的类或其任何父类。
- (BOOL)isKindOfClass:(Class)cls {// 从对象的类开始,tcls将沿着继承链向上遍历。for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {// 检查当前类tcls是否等于要检查的类cls。if (tcls == cls) return YES; // 如果相等,立即返回YES,表示属于该类或其子类。}// 如果遍历完整个继承链都没有找到匹配的类,返回NO。return NO;
}

isKindOfClass分为类方法实例方法相同点都是首先判断调用者的isa指针指向的对象是否和传入的cls对象相同,如果不相同则沿着继承链获取调用者的父类的isa指针接着判断其指向的对象和传入的cls对象相同。

如果相同则返回YES,否则一直沿着继承链找直到tcls为nil退出循环并返回NO。至于tcls为什么能为nil呢,因为任何OC对象沿着继承链向上都会到根类NSObject类而NSObject类的Superclass为nil

说完了相同点下面说下不同点,在OC中实例对象的isa指针指向它所属的类,类对象的isa指针指向它所属的元类。类有类的继承链,元类有元类的继承链,因此会走两条不同的路,但最后又会汇入到一块也就是根类NSObject类。

下面这张图是类和元类的继承链:

class是类,meta是元类。

虚线是isa指针,实线是父类指针。

不难发现类的isa指针指向所属的元类,元类沿着继承链到根元类而根元类的父类是根类(NSObject)

在这里插入图片描述

下面给出isKindOfClass流程图:

类方法调用流程

Class-isKindOfClass.png

实例方法调用流程

Instance-isKindeOfClass.png

总结一下

类对象调用isKindOfClass方法

按照该类所属的元类 --> 根元类 --> 根类 --> nil 与 传入类的对比。

实例对象调用isKindOfClass方法

该对象所属的类 --> 父类 --> 根类 --> nil 与 传入类的对比。

isMemberOfClass

判断该对象是否为传入的类的实例

+ (BOOL)isMemberOfClass:(Class)cls {return self->ISA() == cls;
}- (BOOL)isMemberOfClass:(Class)cls {return [self class] == cls;
}

isMemberOfClass同样也是分为类方法和实例方法。

通过代码不难发现

类方法是判断类对象的isa指针指向的元类对象是否和传入的对象相同

实例方法是判断通过当前对象(self)调用class方法([self class])返回的对象是否和传入的对象相同

这里的class方法我们看下代码:

// 类方法,返回自身
+ (Class)class {return self;
}// 实例方法,查找isa(类)
- (Class)class {return object_getClass(self);
}

实例对象调用的class方法又调用类object_getClass并将自身作为参数传入,接着进入object_getClass方法


Class object_getClass(id obj)
{if (obj) return obj->getIsa();else return Nil;
}

这里又调用了getIsa()方法

inline Class 
objc_object::getIsa() 
{if (!isTaggedPointer()) return ISA();uintptr_t ptr = (uintptr_t)this;if (isExtTaggedPointer()) {uintptr_t slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;return objc_tag_ext_classes[slot];} else {uintptr_t slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;return objc_tag_classes[slot];}
}

这个方法先判断对象是不是TaggedPointer类型,这里涉及到了指针优化的内容,最后又调用了ISA()方法

这里涉及到类与对象底层了,这里就不展开了,总之就是返回到该对象所属的类。

总结一下

类对象调用isMemberOfClass方法

按照该类所属的元类 与 传入的类对比

实例对象调用isMemberOfClass方法

按照该对象所属的类 与 传入的类对比

objc_opt-isKindOfClass

- (void)viewDidLoad {[super viewDidLoad];// Do any additional setup after loading the view.NSLog(@"rel = %d",[[NSObject class] isKindOfClass:[NSObject class]]);
}

运行下面代码并在

NSLog(@"rel = %d",[[NSObject class] isKindOfClass:[NSObject class]]);处加断点,打开汇编调试:Xcode -> Debug -> Debug Workflow -> Always show disassembly。 运行代码可以看到:

在这里插入图片描述

底层调用的不是isKindOfClass方法而是objc_opt-isKindOfClass方法,下面我们看下源码:

BOOL objc_opt_isKindOfClass(id obj, Class otherClass) {
#if __OBJC2__ // 如果是Objective-C 2.0版本及以上if (slowpath(!obj)) return NO; // 慢路径检查,如果对象obj是nil,则直接返回NOClass cls = obj->getIsa(); // 快速获取对象的类信息,ISA指向对象所属的类if (fastpath(!cls->hasCustomCore())) { // 快路径检查,如果类没有自定义的核心实现// 遍历类的继承链,检查是否包含otherClassfor (Class tcls = cls; tcls; tcls = tcls->getSuperclass()) {if (tcls == otherClass) return YES; // 如果在继承链中找到了otherClass,返回YES}return NO; // 如果遍历完整个继承链都没有找到otherClass,返回NO}
#endif // 结束Objective-C 2.0及以上的条件编译// 如果类有自定义的核心实现,或者不满足前面的快路径条件,// 则通过消息发送的方式调用isKindOfClass:方法return ((BOOL(*)(id, SEL, Class))objc_msgSend)(obj, @selector(isKindOfClass:), otherClass);
}

objc_opt-isKindOfClass是对isKindOfClass的方法对优化,首先会慢路径检查判断对象是否存在。接着获取对象所属的类,进行快路径检查判断类有没有自定义核心实现,接着遍历类的继承链并和传入的otherclass做比较。如果类有自定义的核心实现,或者不满足前面的快路径条件,则调用isKindOfClass方法

关于fastpath和slowpath

//x很可能为真, fastpath 可以简称为 真值判断
#define fastpath(x) (__builtin_expect(bool(x), 1)) 
//x很可能为假,slowpath 可以简称为 假值判断
#define slowpath(x) (__builtin_expect(bool(x), 0)) 

__builtin_expect 指令是由 gcc 引入的

目的:编译器可以对代码进行优化,以减少指令跳转带来的性能下降。即性能优化

作用:允许程序员将最有可能执行的分支告诉编译器。

指令的写法为:__builtin_expect(EXP, N) 。表示 EXP==N的概率很大。

fastpath 定义中 __builtin_expect((x),1) 表示 x 的值为真的可能性更大;即 执行if 里面语句的机会更大

slowpath 定义中的 __builtin_expect((x),0) 表示 x 的值为假的可能性更大。即执行 else 里面语句的机会更大

在日常的开发中,也可以通过设置来优化编译器,达到性能优化的目的,设置的路径为:Build Setting --> Optimization Level --> Debug --> 将None 改为 fastest 或者 smallest

相关文章:

【iOS】——探究isKindOfClass和isMemberOfClass底层实现

isKindOfClass 判断该对象是否为传入的类或其子类的实例 // 类方法实现,用于检查一个类是否属于另一个类或其父类链上的任何类。(BOOL)isKindOfClass:(Class)cls {// 从当前类开始,tcls将沿着元类的继承链向上遍历。for (Class tcls self->ISA(); …...

Python 热门面试题(七)

Python中如何拷贝对象?浅拷贝和深拷贝的区别是什么? 在Python中,拷贝对象是一个常见的需求,尤其是当你需要修改一个对象但又不想影响原始对象时。Python提供了几种拷贝对象的方法,其中最重要的是浅拷贝(sh…...

STM32项目分享:智能宠物喂食系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片: 哔哩哔哩视频链接: https://www.bilibili.com/video/BV1zy411z7…...

数据结构——栈的实现(java实现)与相应的oj题

文章目录 一 栈栈的概念:栈的实现:栈的数组实现默认构造方法压栈获取栈元素的个数出栈获取栈顶元素判断当前栈是否为空 java提供的Stack类Stack实现的接口: LinkedList也可以当Stack使用虚拟机栈,栈帧,栈的三个概念 二 栈的一些算…...

linux修改时区为CST

目录 第一步: 第二步: 第三步: 第一步: 备份原来的时区信息 [rootlocalhost ~]# mv /etc/localtime localtime.bak 第二步: 通过软链接将亚洲/上海 的时区信息 指导时区信息 [rootlocalhost ~]# ln -s /usr/share…...

【Spring Security】初识Spring Security

今天晚上因为一个项目问题,而正式开始学习Spring Security。 这个问题是“APP端的操作员应仅可查看管理后台的项目负责人分配给自己的计划”。 一、Spring Security的核心组件: Spring Security的核心组件包括:SecurityContextHolder、Auth…...

配置单区域OSPF

目录 引言 一、搭建基础网络 1.1 配置网络拓扑图如下 1.2 IP地址表 二、测试每个网段都能单独连通 2.1 PC0 ping通Router1所有接口 2.2 PC1 ping通Router1所有接口 2.3 PC2 ping通Router2所有接口 2.4 PC3 ping通Router2所有接口 2.5 PC4 ping通Router3所有接口 2.…...

SQL中的游标是什么?

在 SQL 中,游标(Cursor)是一种用于遍历结果集的数据库对象。它允许开发者在 SQL 查询的结果集中逐行或逐批处理数据。 具体来说,SQL 中的游标通常用于以下目的: 遍历结果集:当一个 SQL 查询返回多行结果时…...

7. LangChain4j如何使用统一api调用?

前言 当我们对接LangChain4j的时候,面对复杂的各种各样的大模型的api的对接,让很多开发者感到力不从心。在每个大模型的api都不一样的时候?该如何快捷的切换模型的使用呢? 这时,One-API应运而生,它以其简洁…...

RPM、YUM 安装 xtrabackup 8 (mysql 热备系列一)包含rpm安装 mysql 8 配置主从

RPM安装 percona-xtrabackup-80-8.0.35-30.1.el7.x86_64.rpm 官网: https://www.percona.com/ 下载地址: https://www.percona.com/downloads wget https://downloads.percona.com/downloads/percona-distribution-mysql-ps/percona-distribution-mysq…...

maven项目打成可运行的jar及pom中的依赖一同打包

maven项目打jar及pom中的依赖一同打包 最近开发中有个需求,不部署新的服务,只jar包执行 那maven项目中,代码如何以jar的方式运行、如何把代码打成jar、pom中的依赖如何与代码一同打到jar包中? 1、代码如何以jar的方式运行&…...

Gettler‘s Screep World 笔记 Ⅰ

夏促时候刚刚入坑,写个笔记叭~ 环境配置 参考 HoPGoldy 大佬的简书,先配置下开发环境 萌新去看大佬的详细教程,我这里比较简单,有前端基础的可以直接抄 VSCode 跳过 node 我配的是v18.18.2 换源 npm config set registry h…...

联合体(union)的定义以及如何与结构体(struct)不同

联合体(Union)是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型。但是,在任何给定的时间点,联合体只能存储其中的一个值;这意味着联合体的大小是其最大成员的大小,因为它必须足够…...

【Spark官方文档部分翻译】RDD编程指南(RDD Programming Guide)

写在前面 内容如何选择 本翻译只翻译本人认为精华的部分,本人认为的Spark的一些核心理念,编程思想。一些特别基础的操作包括但不限于搭建环境就不在此赘述了。 配套版本 本系列基于Spark 3.3.1,Scala 2.12.10,进行翻译总结 原…...

前端八股文 $set

为什么会有$set vue2中对数组中新增的属性是监听不到的 如图 vue 插件中有但是 视图中没有刷新 解决方法 解决就是 $set() 就是在数组中新增属性的时候可以重新渲染视图 具体的写法 写法 就是 第一个 是在那个对象上新增 第二个参数 是新增的属性 第三个参数是 新增的属性…...

Connecting weaviate with langflow across docker containers

题意:在Docker容器之间连接Weaviate与Langflow 问题背景: I am trying to build a local RAG application using Langflow. For my vectore store, I want to use a local Weaviate instance, hosted in a separate docker container on the same netwo…...

【linux vim使用说明】

基本概念 提示:本文是网络资源整理 模式: vim 有多种模式,每种模式都有不同的功能。 普通模式 (Normal Mode): 默认模式,用于导航和执行命令。插入模式 (Insert Mode): 用于文本输入。可以通过按 i 进入。可视模式 (Visual Mode): 用于选择…...

cocos2d-x安装和项目

首先多方查找资料发现教程很简洁,发现对自己的操作方面没多大帮助,后来干脆去官网,好像也很简洁。基于这样一个原因,加上我首次碰cocos2d-x,决定记录一下整个流程,解决实际操作上的疑惑。 涉及的方面&…...

因果推断 | 双重机器学习(DML)算法原理和实例应用

文章目录 1 引言2 DML算法原理2.1 问题阐述2.2 DML算法 3 DML代码实现3.1 策略变量为0/1变量3.2 策略变量为连续变量 4 总结5 相关阅读 1 引言 小伙伴们,好久不见呀。 距离上次更新已经过去了一个半月,上次发文章时还信誓旦旦地表达自己后续目标是3周更…...

Flutter 开源库学习

网上看了好多歌词实现逻辑相关资料,封装比较的好的 就 flutter_lyric,核心类是LyricsReader,而且如果实现逐字逐句歌词编辑功能还需要自己实现很多细节 ,网友原话是 :歌词的功能真的是不少,写起来也是挺难的…...

在软件开发中正确使用MySQL日期时间类型的深度解析

在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...

大型活动交通拥堵治理的视觉算法应用

大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...

大模型多显卡多服务器并行计算方法与实践指南

一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

初学 pytest 记录

安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

tauri项目,如何在rust端读取电脑环境变量

如果想在前端通过调用来获取环境变量的值&#xff0c;可以通过标准的依赖&#xff1a; std::env::var(name).ok() 想在前端通过调用来获取&#xff0c;可以写一个command函数&#xff1a; #[tauri::command] pub fn get_env_var(name: String) -> Result<String, Stri…...

WEB3全栈开发——面试专业技能点P7前端与链上集成

一、Next.js技术栈 ✅ 概念介绍 Next.js 是一个基于 React 的 服务端渲染&#xff08;SSR&#xff09;与静态网站生成&#xff08;SSG&#xff09; 框架&#xff0c;由 Vercel 开发。它简化了构建生产级 React 应用的过程&#xff0c;并内置了很多特性&#xff1a; ✅ 文件系…...

医疗AI模型可解释性编程研究:基于SHAP、LIME与Anchor

1 医疗树模型与可解释人工智能基础 医疗领域的人工智能应用正迅速从理论研究转向临床实践,在这一过程中,模型可解释性已成为确保AI系统被医疗专业人员接受和信任的关键因素。基于树模型的集成算法(如RandomForest、XGBoost、LightGBM)因其卓越的预测性能和相对良好的解释性…...