序言
- Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
- Objective-C的动态性是由Runtime API来支撑的
- Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写
位运算和共用体
位运算
程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算说穿了,就是直接对整数在内存中的二进制位进行操作。
C++提供了6种位运算符来进行位运算操作:
- & 按位与
- | 按位或
- ^ 按位异或
- ~ 按位取反
- << 左移(左边消失,右边补0)
- >> 右移(右边消失,左边补符号位)
位运算的操作数是整数类型或字符型.
1.按位与&运算
相同为一
& 运算常常用来将某变量的某些位清0
& 也常用于二进制取位操作
2.按位或|运算
有一则一
|运算通常用于二进制特定位上的强制置1
3.按位异或^运算
^运算通常用于对二进制的特定一位进行取反操作
共用体
共用体把几种不同数据类型的变量存放在同一块内存里。共用体中的变量共享同一块内存。
定义共用体类型变量的一般形式:
union 共用体名
{
成员列表
}变量列表;
union的主要特征有
- union中可以定义多个成员,union的大小由最大的成员的大小决定;
- union成员共享同一块大小的内存,一次只能使用其中的一个成员;
- 对union某一个成员赋值,会覆盖其他成员的值(但前提是成员所占字节数相同,当成员所占字节数不同时只会覆盖相应字节上的值,比如对char成员赋值就不会把整个int成员覆盖掉,因为char只占一个字节,而int占四个字节);
- union量的存放顺序是所有成员都从低地址开始存放的。
一 isa详解
- 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
- 在arm64架构之前,isa就是一个普通的
指针
,存储着Class、Meta-Class对象的内存地址
- 从arm64架构开始,对isa进行了优化,变成了一个
共用体(union)
结构,还使用位域
来存储更多的信息
- isa结构体
/** isa_t 结构体 */
union isa_t {
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33;
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
};
实例代码如下 - 一定需要用真机进行调试
#import <Foundation/Foundation.h>
@interface CSPerson : NSObject
@end
创建一个对象并且断点调试,输出 isa 值
接下来我们创建一些关联对象和弱引用对象
isa参数详解
nonpointer
:
0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
1,代表优化过,使用位域存储更多的信息has_assoc
:是否有设置过关联对象,如果没有,释放时会更快has_cxx_dtor
:是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快shiftcls
:存储着Class、Meta-Class对象的内存地址信息magic
:用于在调试时分辨对象是否未完成初始化weakly_referenced
:是否有被弱引用指向过,如果没有,释放时会更快deallocating
:对象是否正在释放extra_rc
:表示该对象的引用计数值,实际上是引用计数值减 1,例如,如果对象的引用计数为 10,那么 extra_rc 为 9。如果引用计数大于 10,则需要使用到下面的 has_sidetable_rc。has_sidetable_rc
:当对象引用计数大于 10 时,则has_sidetable_rc 的值为 1,那么引用计数会存储在一个叫 SideTable 的类的属性中,这是一个散列表。
为什么要&ISA_MASK来获取类或元类的地址
因为从arm64位开始,isa里面存储各种信息,是一个共用体,其中shiftcls
33位才是用来存放地址。通过&ISA_MASK就可以将33位的地址值取出来。
无论是实例对象,还是类对象,还是元类对象,他们的地址最后一位要么是0,要么是8,因为他们的地址是
isa
&ISA_MASK
,又因为ISA_MASK
最后3位都是0,所以导致他们的地址最后3位也永远是0,所以最后一位要么是0(0000 0000)
,要么是8(0000 1000)
。
本文主要参考MJ老师的教案,非常感谢MJ老师。
关于runtime更多文章请看如下链接
iOS-runtime-API详解+使用
iOS Runtime原理及使用
iOS - runtime如何通过selector找到对应的 IMP地址(分别考虑类方法和实例方法)
iOS - Runtime之面试题详解一
iOS-runtime之面试题详解二
iOS runtime的使用场景-实战篇