OC源码剖析对象的本质
1. 类的底层实现
先写一个 Person 类:
@interface Person : NSObject
@property (nonatomic, copy) NSString *p_name;
@property (nonatomic, assign) int p_age; - (void)p_instanceMethod1;
@end @implementation Person
- (void)p_instanceMethod1{
NSLog(@"%s",__func__);
}
@end
使用 clang 编译器, clang -rewrite-objc Person.m -o Person.cpp 将 Person.m 编译成 Person.cpp 文件,部分代码如下:
/// 1: Person 类型的底层结构
struct NSObject_IMPL {
Class isa;
}; struct Person_IMPL {
struct NSObject_IMPL NSObject_IVARS;
int _p_age;
NSString * _Nonnull _p_name;
}; /// 2: p_name 属性的底层结构
// get
static NSString * _Nonnull _I_Person_p_name(Person * self, SEL _cmd) { return (*(NSString * _Nonnull *)((char *)self + OBJC_IVAR_$_Person$_p_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);
// set
static void _I_Person_setP_name_(Person * self, SEL _cmd, NSString * _Nonnull p_name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct Person, _p_name), (id)p_name, 0, 1); } /// 3: p_age 类型的底层结构
// get
static int _I_Person_p_age(Person * self, SEL _cmd) { return (*(int *)((char *)self + OBJC_IVAR_$_Person$_p_age)); }
// set
static void _I_Person_setP_age_(Person * self, SEL _cmd, int p_age) { (*(int *)((char *)self + OBJC_IVAR_$_Person$_p_age)) = p_age; } /// 4: p_instanceMethod1 方法的底层结构
static void _I_Person_p_instanceMethod1(Person * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_86_0y_j3bzj65z6vw6hy1chw_4m0000gp_T_Person_f010c0_mi_0,__func__);
}
- NSObject 类被编译成了 NSObject_IMPL 的结构体。
- Person 类被编译成了 Person_IMPL 的结构体。
- Person 类的内部还增加了一个 NSObject_IMPL 的结构体
- 我们知道 Person 继承于 NSObject, 所以它的底层实现中是第一个成员是父类的结构体,就是底层继承的实现方式。用这样的方式拥有父类所有的成员变量。
- NSObject_IMPL 是 NSObject 类的编译后的结构体,它的内部只有一个 Class 类型的 isa 成员变量。我们知道 isa 是 isa_t 类型的,那为什么在这里定义成 Class 类型呢?这是为了更加直观的提现出它代表的是类的信息,所以在获取isa 的方法中,将它强制转换成了Class 类型, 代码如下:
inline Class objc_object::ISA() {
...
return (Class)(isa.bits & ISA_MASK)
}
总结:
1.类的底层实现是结构体。
2.继承是通过把父类的结构体声明为本类结构体的第一个成员变量实现的。
2. isa_t 的类型
联合体: 所有成员可以是不同的类型,但是公用一块内存区域,设置了一个成员变量就会覆盖另一个成员变量的数据。优点是节省空间。
union isa_t { //联合体
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
//提供了cls 和 bits ,两者是互斥关系
Class cls;
uintptr_t bits;
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
#endif
};
isa 指针占用8字节,64位。64位中不同的位代表不同的含义:

对象与类的 isa 的指向关系

对象.isa -> 类.super -> 父类.super -> 根类.super -> nil
类.isa -> 元类.super -> 父元类.super -> 根元类.super -> 根类.super -> nil
元类.isa = 父元类.isa = 根元类.isa = 根元类
应用:判断对象类型
下面的打印结果是什么:
// [NSObject class] = NSObject
// object_getClass((id)[NSObject class]) = NSObject meta class // 沿着 NSObject 的继承者链去找根元类 -> 根类 == NSObject meta class 或者 NSObject meta class 的父类的实例
BOOL res1 = [(id)[NSObject class] isKindOfClass:[NSObject class]];
BOOL res2 = [(id)[NSObject class] isMemberOfClass:[NSObject class]];
BOOL res3 = [(id)[TestObject class] isKindOfClass:[TestObject class]];
BOOL res4 = [(id)[TestObject class] isMemberOfClass:[TestObject class]];
只有第一个是YES, 剩下的都是NO。
isKindOfClass: 判断自己的isa 指向的类是否等于传入的类,不等于的话,找自己的继承连中的父类看有没有等于传入的类,有则YES,没有则NO
isMemberOfClass 判断自己的isa 指向的类是否等于传入的类,等于则YES,不等于则NO
源码:
// 类对象,是否是指定的元类的实例
+ (BOOL)isMemberOfClass:(Class)cls {
return object_getClass((id)self) == cls;
} // 实例对象,是否是指定的类的实例
- (BOOL)isMemberOfClass:(Class)cls {
return [self class] == cls;
} // 类对象,是否是指定的元类cls的实例,或者是cls继承者链中子类的实例
+ (BOOL)isKindOfClass:(Class)cls {
for (Class tcls = object_getClass((id)self); tcls; tcls = tcls->super_class) {
if(tcls == cls) return YES;
}
return NO;
}
// 实例对象,是否是指定的类的实例,或者是cls继承者链中子类的实例
-(BOOL)isKindOfClass:(Class)cls {
for(Class tcls = [self class]; tcls; tcls = tcls->super_class) {
if(tcls == cls) return YES;
}
return NO;
}
青山不改,绿水长流,感谢每位佳人支持!
OC源码剖析对象的本质的更多相关文章
- java源码剖析: 对象内存布局、JVM锁以及优化
一.目录 1.启蒙知识预热:CAS原理+JVM对象头内存存储结构 2.JVM中锁优化:锁粗化.锁消除.偏向锁.轻量级锁.自旋锁. 3.总结:偏向锁.轻量级锁,重量级锁的优缺点. 二.启蒙知识预热 开启 ...
- Python源码剖析——01内建对象
<Python源码剖析>笔记 第一章:对象初识 对象是Python中的核心概念,面向对象中的"类"和"对象"在Python中的概念都为对象,具体分为 ...
- 老李推荐:第2章4节《MonkeyRunner源码剖析》了解你的测试对象: NotePad窗口Activity之菜单简介
老李推荐:第2章4节<MonkeyRunner源码剖析>了解你的测试对象: NotePad窗口Activity之菜单简介 NotePad窗口Activity之菜单简介 这里我们总共用到 ...
- 老李推荐:第2章3节《MonkeyRunner源码剖析》了解你的测试对象: NotePad窗口Activity之NoteEditor简介
老李推荐:第2章3节<MonkeyRunner源码剖析>了解你的测试对象: NotePad窗口Activity之NoteEditor简介 我们在增加和编辑一个日记的时候会从NotesL ...
- 老李推荐:第2章2节《MonkeyRunner源码剖析》了解你的测试对象: NotePad窗口Activity之NotesList简介
老李推荐:第2章2节<MonkeyRunner源码剖析>了解你的测试对象: NotePad窗口Activity之NotesList简介 NotePad窗口Activity之NotesL ...
- 老李推荐:第2章1节《MonkeyRunner源码剖析》了解你的测试对象: NotePad应用简介
老李推荐:第2章1节<MonkeyRunner源码剖析>了解你的测试对象: NotePad应用简介 本书脚本相关的示例常会用到Android SDK自带的NotePad这个应用,所以这 ...
- Python 源码剖析(一)【python对象】
处于研究python内存释放问题,在阅读部分python源码,顺便记录下所得.(基于<python源码剖析>(v2.4.1)与 python源码(v2.7.6)) 先列下总结: ...
- Python开发【源码剖析】 List对象
前言 本文探讨的Python版本为2.7.16,可从官网上下载,把压缩包Python-2.7.16.tgz解压到本地即可 需要基本C语言的知识(要看的懂) PyListObject对象 PyListO ...
- Node 进阶:express 默认日志组件 morgan 从入门使用到源码剖析
本文摘录自个人总结<Nodejs学习笔记>,更多章节及更新,请访问 github主页地址.欢迎加群交流,群号 197339705. 章节概览 morgan是express默认的日志中间件, ...
随机推荐
- Blind SQL injection:盲注详解
什么是盲注? 当应用程序易受SQL注入攻击,但其HTTP响应不包含相关SQL查询的结果或任何数据库错误的详细信息时,就会出现盲SQL注入. 对于盲目SQL注入漏洞,许多技术(如联合攻击)都是无效的,因 ...
- STM32—重定向printf和getchar函数到串口
在STM32测试串口的时候经常需要在开发板和上位机之间传输数据,我们可以用c语言中的printf()函数和getchar()函数来简化传输. 以printf()为例: printf()函数实际上是一个 ...
- 10、二进制安装K8s之部署CoreDNS 和Dashboard
二进制安装K8s之部署CoreDNS 和Dashboard CoreDNS 和Dashboard 的yaml文件在 k8s源代码压缩包里面可以找到对应的配置文件,很多人从网上直接下载使用别人的,会导致 ...
- MySQL:互联网公司常用分库分表方案汇总!
转载别人 一.数据库瓶颈 不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值.在业务Service来看就是,可用数据库连接少甚至无连接可用 ...
- c#创建windows服务入门教程实例
用c#中创建一个windows服务非常简单,与windows服务相关的类都在System.ServiceProcess命名空间下. 每个服务都需要继承自ServiceBase类,并重写相应的启动.暂停 ...
- 【springcloud】Zuul高级配置(zuul--2)
转自:https://blog.csdn.net/pengjunlee/article/details/87162192 自定义路由规则 在<API Gateway 的路由和过滤(Zuul)&g ...
- 【springcloud】Eureka服务注册中心搭建
转自:https://blog.csdn.net/pengjunlee/article/details/86538997 Spring Cloud是一系列框架的集合,它利用Spring Boot的开发 ...
- 传统JIT和java9新特性AOT理解
java慢的原因 1. 除了少量基本类型用栈存储外,所有对象都使用堆存储.堆的性能低于栈. 2. 很多强制类型转换(cast)或加查,耗用内存大.java运行时对类型检测,如果类型不正确会抛出Cl ...
- JDBC中的元数据——3.结果集元数据
package metadata; import java.sql.Connection; import java.sql.ParameterMetaData; import java.sql.Pre ...
- SpringBoot博客开发之异常处理
异常处理: 背景: 最近在搭建属于自己的个人博客(码农小白的执念),自己搭建后端的时候首先考虑的是异常处理.个人也是一边学习一边做,难免有疏漏的地方,希望朋友们在不对的地方提醒下. 技术栈: spri ...