1.对象,isa,类和元类
1.1.概念
- 每个
对象都有一个isa
,在对象分配内存时,内存的第0-7字节
存储的就是isa信息
- 对象的isa中包含了对象
所属类
的信息 - 每个类也有一个isa,也存在类内存的
第0-7字节
,因为类也是一个对象
,类是元类的对象
,元类是对类对象的描述
,就像类是普通实例对象的描述一样,类的归属来着于元类
- 元类的
定义和创建
由编译器完成
,元类本与类相关联,名称和类名同名
. NSObject的根元类
指向NSObject根元类
,根元类的isa指向自己
根元类的地址是0x00000001003340f0,而根元类的isa & mask = isa(0x00000001003340f0 & 0x00007ffffffffff8ULL= 0x00000001003340f0),验证了根元类的isa指向自己
1.2 继承关系
- 在OC中,
NSObject的父类是nil
,如果类存在继承关
系,则类的元类也存在继承关系
例如:
当有Student类继承自Person类,Person类继承自NSObject时,Student类的元类继承自Person类的元类,Person类的元类继承自NSObject的元类,NSObject的元类就是NSObject本身
-
类
之间 的继承
关系:类(subClass)
继承自父类(superClass)
父类(superClass)
继承自根类(Root Class)
,此时的根类是指NSObject
根类
继承自nil
,所以根类
即NSObject
可以理解为万物起源
,即无中生有
-
元类
也存在继承
,元类之间的继承关系如下:子类的元类(metal SubClass)
继承自父类的元类(metal SuperClass)
父类的元类(metal SuperClass)
继承自根元类(Root metal Class
根元类(Root metal Class)
继承于根类(Root class)
,此时的根类是指NSObject
- 对象,类,元类之间的
继承关系
:继承关系是相对于类和元类
,对象之间不存在继承关系
图转自LG-Cooci老师
由此可知,**isa的走位关系
**图如下:
实例对象(Instance of Subclass)
的isa
指向类(class)
类对象(class)
isa
指向元类(Meta class)
元类(Meta class)
的isa
指向根元类(Root metal class)
根元类(Root metal class)
的isa
指向它自己
本身,形成闭环
,这里的根元类
就是NSObject
实例分析:Person继承自NSObject, Student继承自Person
-
isa 走位链(两条)
- student的isa走位链:
student(子类对象) --> Student (子类)--> Student(子元类) --> NSObject(根元类) --> NSObject(跟根元类,即自己)
- person的isa走位图:
person(父类对象) --> Person (父类)--> Person(父元类) --> NSObject(根元类) --> NSObject(跟根元类,即自己)
- student的isa走位链:
-
superclass走位链(两条)
- 类的继承关系链:
Student(子类) --> Person(父类) --> NSObject(根类)--> nil
- 元类的继承关系链:
Student(子元类) --> Person(父元类) --> NSObject(根元类)--> NSObject(根类)--> nil
- 类的继承关系链:
1.3 类编译后在内存中只存在一份
//MARK: - 分析类在内存存在个数
void lgTestClassNum(){
Class class1 = [HTPerson class];
Class class2 = [HTPerson alloc].class;
Class class3 = object_getClass([HTPerson alloc]);
Class class4 = [HTPerson alloc].class;
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
/*
打印结果:
0x100008100-
0x100008100-
0x100008100-
0x100008100
*/
}
上述代码中,通过三种不同方式创建LGPerson三个类对象,但三个类对象的地址一模一样,说明类在内存中只有一个
.
注意:特殊NSObjec类
// NSObject实例对象
NSObject *object1 = [NSObject alloc];
// NSObject类
Class class = object_getClass(object1);
// NSObject元类
Class metaClass = object_getClass(class);
// NSObject根元类
Class rootMetaClass = object_getClass(metaClass);
// NSObject根根元类
Class rootRootMetaClass = object_getClass(rootMetaClass);
NSLog(@"\n%p 实例对象\n%p 类\n%p 元类\n%p 根元类\n%p 根根元类",object1,class,metaClass,rootMetaClass,rootRootMetaClass);
/*打印结果:
0x10070d950 实例对象
0x100357140 类
0x1003570f0 元类
0x1003570f0 根元类
0x1003570f0 根根元类
*/
在内存中,NSObject类也只存在一份,NSObject的元类和类同名,也存在一份,NSObject的元类、根元类、根根元类相同,NSObject的元类的isa指向自己
2 objc_object 和 objc_class
所有的类都是objc_class的对象,objc_class继承自结构体objc_object
//注意:源码来自objc4-781 objc_runtime_new.h
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache;
class_data_bits_t bits;
class_rw_t *data() const {
return bits.data();
}
void setData(class_rw_t *newData) {
bits.setData(newData);
}
//...
}
注意:源码来自objc4-781 objc_runtime_new.h
objc_class 与 objc_object 有什么关系?
结构体类型
objc_class
继承自objc_object
类型,其中objc_object
也是一个结构体,且有一个isa
属性,所以objc_class
也拥有了isa
属性mian.cpp底层编译文件中,
NSObject
中的isa
在底层是由Class
定义的,其中class
的底层编码来自objc_class
类型,所以NSObject
也拥有了isa
属性
NSObject
是一个类,用它初始化一个实例对象objc
,objc 满足objc_object
的特性(即有isa属性),主要是因为isa
是由NSObject
从objc_class
继承过来的,而objc_class
继承自objc_object
,objc_object
有isa
属性。所以对象
都有一个isa
,isa表示指向
,来自于当前的objc_object
objc_object(结构体)
是 当前的根对象
,所有的对象
都有这样一个特性objc_object
,即拥有isa属性
- objc_object是
根对象
,是一个结构体,objc_object含有一个isa
的成员.所以元类,类,对象都有isa
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
objc_object 与对象的关系:
所有的对象都是以objc_object为模板实例化出来的;OC端类的根类是NSObject,NSObject在底层的模板是objc_object
OC代码都会编译成C++代码,
OC中的Class(isa指针的类型)的底层模板是objc_class
: typedef struct objc_class *Class;
3 类结构分析
3.1 内存偏移
存在在栈内存中的数据地址连续
:
- 普通
基本类型的变量
的地址偏移
int a = 10;
int b = 10;
printf("%d--%p\n",a,&a);
printf("%d--%p\n",b,&b);
/*
打印结果:
10--0x7ffeefbff4f0
10--0x7ffeefbff4f4
*/
在内存中,常量区有个地址空间存在着常量10
,定义a、b两个变量在编译时,编译int a = 10会将常量10进行值拷贝
到变量a中,编译int b = 10将常量10进行值拷贝
到变量b中,但是存储变量a、b的地址
不一样.
a,b存储在栈中
,地址连续
,地址相处a的类型
所在需要的内存空间,例子中a是int型,所有a和b的地址相差4个字节
类对象的变量
地址偏移
LGPerson *p1 = [LGPerson alloc]; // p1 是指针
LGPerson *p2 = [LGPerson alloc];
NSLog(@"%@ -- %p\n", p1, &p1);
NSLog(@"%@ -- %p\n", p2, &p2);
/*
打印结果:
<LGPerson: 0x10180f430> -- 0x7ffeefbff4e0
<LGPerson: 0x10180f730> -- 0x7ffeefbff4e8
*/
p1、p2 是指针,p1 ,p1是 指向 [LGPerson alloc]创建的空间地址 &p1、&p2是 指向 p1、p2对象指针的地址,二级指针
数组指针
地址偏移
int c[4] = {1, 2, 3, 4};
int *d = c;
NSLog(@"%p -- %p - %p",&c[0], &c[1],&c[2]);
NSLog(@"%p -- %p - %p", d, d+1, d+2);
/*
0x7ffeefbff4e0 -- 0x7ffeefbff4e4 - 0x7ffeefbff4e8
0x7ffeefbff4e0 -- 0x7ffeefbff4e4 - 0x7ffeefbff4e8
*/
数组元素的地址是连续的,且&c[0] 与 &c[1] 相差4个字节,&c[1] 与 &c[2] 相差4个字节,地址之间相差的字节数,由存储的数据类型决定
,由于存储的是int型,所以是相差4个字节
可以通过 首地址+偏移量
取出数组中的其他元素,其中偏移量是数组的下标,实际取的地址是数组首地址+ 偏移量 * 数据类型字节数
3.2 objc_class类结构
objc4-781 Project Headers/objc_runtime-new.h定义objc_class结构如下:
-
isa
属性:Class类型
的的isa,占8字节
-
superclass
属性:Class类型
,Class是objc_object指针,占8字节
-
cache
属性:cache_t结构体类型
对象,占用16个字节
struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic<struct bucket_t *> _buckets; // 是一个结构体指针类型,占8字节
explicit_atomic<mask_t> _mask; //是mask_t 类型,而 mask_t 是 unsigned int 的别名,占4字节
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic<uintptr_t> _maskAndBuckets; //是指针,占8字节
mask_t _mask_unused; //是mask_t 类型,而 mask_t 是 uint32_t 类型定义的别名,占4字节
#if __LP64__
uint16_t _flags; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
#endif
uint16_t _occupied; //是uint16_t类型,uint16_t是 unsigned short 的别名,占 2个字节
#if 分支计算内存
buckets
类型是struct bucket_t *,是结构体指针类型,占8字节
mask
是mask_t 类型,而 mask_t , unsigned int 的别名,占4字节
_flags
是uint16_t类型,占 2个字节
_occupied
是uint16_t类型,占 2个字节
elseif分支计算内存
_maskAndBucket
s 是uintptr_t类型,是指针,占8字节
_mask_unused
是mask_t 类型,而 mask_t 是 uint32_t 类型定义的别名,占4字节
_flags
是uint16_t类型,占 2个字节
_occupied
是uint16_t类型,占 2个字节
bits
属性:class_data_bits_t类型,是个结构体,结构体的内存大小需要根据内部的属性来确定,首地址经过上面前3个属性的内存大小总和32个字节平移,就可以获取到bits的首地址
3.3 获取bits的地址
3.3.1 通过类的首地址平移32字节
获取 class_data_bits_t*:
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) x/4gx 0x0000000100002240
0x100002240: 0x0000000100002218 0x0000000100334140
0x100002250: 0x00000001006847d0 0x000580240000000f
(lldb) p 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 4294976024
(lldb) p/x 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100002218
(lldb) p (class_data_bits_t*)0x0000000100002238
(class_data_bits_t *) $3 = 0x0000000100002238
3.3.2 class_data_bits_t通过data()方法获取class_rw_t结构体指针
- class_data_bits_t结构
- 命令获取:
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) x/4gx 0x0000000100002240
0x100002240: 0x0000000100002218 0x0000000100334140
0x100002250: 0x00000001006847d0 0x000580240000000f
(lldb) p 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 4294976024
(lldb) p/x 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100002218
(lldb) p (class_data_bits_t*)0x0000000100002238
(class_data_bits_t *) $3 = 0x0000000100002238
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100683dc0
(lldb) p $4->methods()
3.3.4 class_rw_t通过methods(),properties(),protocols()获取方法列表,属性列表,协议列表
- class_rw_t结构定义
- 命令获取类方法列表(类方法存储在元类中,从元类中获取)
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) x/4gx 0x0000000100002240
0x100002240: 0x0000000100002218 0x0000000100334140
0x100002250: 0x00000001006847d0 0x000580240000000f
(lldb) p 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 4294976024
(lldb) p/x 0x0000000100002218 & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x0000000100002218
(lldb) p (class_data_bits_t*)0x0000000100002238
(class_data_bits_t *) $3 = 0x0000000100002238
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100683dc0
(lldb) p $4->methods()
(const method_array_t) $5 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x0000000100002090
arrayAndFlag = 4294975632
}
}
}
(lldb) p $5.list
(method_list_t *const) $6 = 0x0000000100002090
(lldb) p *$6
(method_list_t) $7 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 26
count = 1
first = {
name = "sayBye"
types = 0x0000000100000f6e "v16@0:8"
imp = 0x0000000100000d20 (KCObjc`+[LGPerson sayBye])
}
}
}
(lldb) p $7.get(0)
(method_t) $8 = {
name = "sayBye"
types = 0x0000000100000f6e "v16@0:8"
imp =
- 命令获取对象方法(从类结构中获取)
lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) p/x (class_data_bits_t *)(0x0000000100002260)
(class_data_bits_t *) $1 = 0x0000000100002260
(lldb) p/x $1->data()
(class_rw_t *) $2 = 0x0000000100656070
(lldb) p/x $2->methods()
(const method_array_t) $3 = {
list_array_tt<method_t, method_list_t> = {
= {
list = 0x00000001000020f8
arrayAndFlag = 0x00000001000020f8
}
}
}
(lldb) p $3.list
(method_list_t *const) $4 = 0x00000001000020f8
(lldb) p *$4
(method_list_t) $8 = {
entsize_list_tt<method_t, method_list_t, 3> = {
entsizeAndFlags = 16
count = 2
first = {
name = "name"
types = 0x0000000100000ee1 "T@\"NSString\",C,N,V_name"
imp = 0x0000000100000ef9 ("age")
}
}
}
(lldb)p $8.get(0)
(method_t) $9 = {
name = "name"
types = 0x0000000100000ee1 "T@\"NSString\",C,N,V_name"
imp = 0x0000000100000ef9 ("age")
}
(lldb) p $8.get(1)
(method_t) $10 = {
name = "age"
types = 0x0000000100000efd "Tq,N,V_age"
imp = 0x0000000100002240 ((void *)0x0000000100002218: LGPerson)
}
(lldb) p $8.get(2)
Assertion failed: (i < count), function get, file /Users/wangqiao/Desktop/iOSV9.0/可编译objc源码/runtime/objc-runtime-new.h, line 434.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
- 命令获取对象属性(从类结构中获取)
(lldb) p/x LGPerson.class
(Class) $0 = 0x0000000100002240 LGPerson
(lldb) p/x (class_data_bits_t *)(0x0000000100002260)
(class_data_bits_t *) $1 = 0x0000000100002260
(lldb) p/x $1->data()
(class_rw_t *) $2 = 0x0000000100656070
(lldb) p/x $2->properties()
(const property_array_t) $5 = {
list_array_tt<property_t, property_list_t> = {
= {
list = 0x00000001000021d8
arrayAndFlag = 0x00000001000021d8
}
}
}
(lldb) p/x $5.list
(property_list_t *const) $6 = 0x00000001000021d8
(lldb) p *$6
(property_list_t) $7 = {
entsize_list_tt<property_t, property_list_t, 0> = {
entsizeAndFlags = 16
count = 2
first = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
}
}
(lldb) p $7.get(0)
(property_t) $11 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $7.get(1)
(property_t) $12 = (name = "age", attributes = "Tq,N,V_age")
(lldb) p $7.get(2)
Assertion failed: (i < count), function get, file /Users/wangqiao/Desktop/iOSV9.0/可编译objc源码/runtime/objc-runtime-new.h, line 434.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
- 命令获取属性列表(不包含成员变量)
-
通过objc_class类的地址便宜32个字节,得到bits的地址,获取到
class_data_bits_t
的地址class_data_bits_t*
-
在
class_data_bits_t
中调用data()
,获取到class_rw_t
的地址class_rw_t*
-
class_rw_t
结构中调用ro()
,获取到class_ro_t
的地址`class_ro_t*) -
class_ro_t结构中的baseProperties成员变量,就是属性列表的地址
-
llvm命令获取
属性列表
(同理可以获取成员变量列表
和方法列表
)
-
- 通过bits->data->ro 获取ivars(包含属性和成员变量)
(lldb) x/4gx person
0x100647220: 0x011d8001000083dd 0x0000000000000000
0x100647230: 0x0000000000000000 0x0000000000000000
(lldb) p 0x011d8001000083dd & 0x00007ffffffffff8ULL
(unsigned long long) $1 = 4295001048
(lldb) p/x 0x011d8001000083dd & 0x00007ffffffffff8ULL
(unsigned long long) $2 = 0x00000001000083d8
(lldb) p (class_data_bits_t *)0x00000001000083f8
(class_data_bits_t *) $3 = 0x00000001000083f8
(lldb) p $3->data()
(class_rw_t *) $4 = 0x0000000100646670
(lldb) p $4->ro()
(const class_ro_t *) $5 = 0x0000000100008098
(lldb) p *$5
(const class_ro_t) $6 = {
flags = 388
instanceStart = 8
instanceSize = 72
reserved = 0
= {
ivarLayout = 0x0000000100003ede "\x01\x11\x13"
nonMetaclass = 0x0000000100003ede
}
name = {
std::__1::atomic<const char *> = "HTPerson" {
Value = 0x0000000100003ed5 "HTPerson"
}
}
baseMethodList = 0x00000001000080e0
baseProtocols = 0x0000000000000000
ivars = 0x0000000100008190
weakIvarLayout = 0x0000000000000000
baseProperties = 0x0000000100008298
_swiftMetadataInitializer_NEVER_USE = {}
}
(lldb) p $6.ivars
(const ivar_list_t *const) $7 = 0x0000000100008190
(lldb) p $7->get(0)
(ivar_t) $8 = {
offset = 0x0000000100008370
name = 0x0000000100003eec "hobby"
type = 0x0000000100003f60 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7->get(1)
(ivar_t) $9 = {
offset = 0x0000000100008378
name = 0x0000000100003ef2 "weight"
type = 0x0000000100003f6c "i"
alignment_raw = 2
size = 4
}
(lldb) p $7->get(2)
(ivar_t) $10 = {
offset = 0x0000000100008380
name = 0x0000000100003ef9 "objc"
type = 0x0000000100003f6e "@\"NSObject\""
alignment_raw = 3
size = 8
}
(lldb) p $7->get(3)
(ivar_t) $11 = {
offset = 0x0000000100008388
name = 0x0000000100003efe "sex"
type = 0x0000000100003f7a "c"
alignment_raw = 0
size = 1
}
(lldb) p $7->get(4)
(ivar_t) $12 = {
offset = 0x0000000100008390
name = 0x0000000100003f02 "intersting"
type = 0x0000000100003f60 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7->get(5)
(ivar_t) $13 = {
offset = 0x0000000100008398
name = 0x0000000100003f0d "_name"
type = 0x0000000100003f60 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7->get(6)
(ivar_t) $14 = {
offset = 0x00000001000083a0
name = 0x0000000100003f13 "_nickname"
type = 0x0000000100003f60 "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $7->get(7)
(ivar_t) $15 = {
offset = 0x00000001000083a8
name = 0x0000000100003f1d "_age"
type = 0x0000000100003f7c "q"
alignment_raw = 3
size = 8
}
3.4 OC层通过runtime获取实例变量名称,属性的名称
void lgObjc_copyIvar_copyProperies(Class pClass){
unsigned int count = 0;
Ivar *ivars = class_copyIvarList(pClass, &count);
for (unsigned int i=0; i < count; i++) {
Ivar const ivar = ivars[i];
//获取实例变量名
const char*cName = ivar_getName(ivar);
NSString *ivarName = [NSString stringWithUTF8String:cName];
LGLog(@"class_copyIvarList:%@",ivarName);
}
free(ivars);
unsigned int pCount = 0;
objc_property_t *properties = class_copyPropertyList(pClass, &pCount);
for (unsigned int i=0; i < pCount; i++) {
objc_property_t const property = properties[i];
//获取属性名
NSString *propertyName = [NSString stringWithUTF8String:property_getName(property)];
LGLog(@"class_copyProperiesList:%@",propertyName);
}
free(properties);
}