一,内存偏移
1,普通指针研究
int a = 10;
int b = 10;
int *a_p = &a;
int *b_p = &b;
NSLog(@"%d -- %p -- %p",a,&a,&a_p);
NSLog(@"%d -- %p -- %p",b,&b,&b_p);
//输出结果
2021-07-26 16:15:17.348698+0800 内存偏移[7982:190211] 10 -- 0x7ffee841a03c -- 0x7ffee841a030
2021-07-26 16:15:17.348855+0800 内存偏移[7982:190211] 10 -- 0x7ffee841a038 -- 0x7ffee841a028
- a和b的值都是10 但是a和b地址不一样,整型赋值是
值拷贝a和b属于两份数据 - a地址是0x7ffee841a03c,b地址是0x7ffee841a038偏移量是4, 因为a,b是
int类型 - a_p地址是0x7ffee841a030,b_p地址是0x7ffee841a028偏移量是8,因为a_p,b_p是
(int*)类型 - 地址大小比较:a > b > a_p > b_p,由于是局部变量,他们都存放在
栈区!
2,对象指针研究
NBPerson *p1 = [NBPerson alloc];
NBPerson *p2 = [NBPerson alloc];
NBPerson *p3 = p2;
NSLog(@"---%@----%p",p1,&p1);
NSLog(@"---%@----%p",p2,&p2);
NSLog(@"---%@----%p",p3,&p3);
//输出结果
2021-07-26 18:04:00.553396+0800 内存偏移[10054:263449] ---<NBPerson: 0x6000012ac2e0>----0x7ffee47bb038
2021-07-26 18:04:00.553507+0800 内存偏移[10054:263449] ---<NBPerson: 0x6000012ac2f0>----0x7ffee47bb030
2021-07-26 18:04:00.553599+0800 内存偏移[10054:263449] ---<NBPerson: 0x6000012ac2f0>----0x7ffee47bb028
alloc开辟的内存在堆区,局部变量开辟的内存在栈区- 堆区
低地址 -->高地址,栈区高地址 -->低地址 - p3和p2存的都是第二次alloc的地址属于
同一份数据
3,数组指针研究
int c[4] = {1,2,3,4};
int *d = c;
NSLog(@"%p - %p - %p ",&c,&c[0],&c[1]);
NSLog(@"%p - %p - %p ",d,d+1,d+2);
for (int i = 0; i<4; i++) {
//(d+i) 取地址里面的值
int value = *(d+i);
NSLog(@"value = %d",value);
}
//输出结果
2021-07-26 18:17:57.673897+0800 内存偏移[10303:272382] 0x7ffeef40c040 - 0x7ffeef40c040 - 0x7ffeef40c044
2021-07-26 18:17:57.674018+0800 内存偏移[10303:272382] 0x7ffeef40c040 - 0x7ffeef40c044 - 0x7ffeef40c048
2021-07-26 18:17:57.674109+0800 内存偏移[10303:272382] value = 1
2021-07-26 18:17:57.674187+0800 内存偏移[10303:272382] value = 2
2021-07-26 18:17:57.674255+0800 内存偏移[10303:272382] value = 3
2021-07-26 18:17:57.674330+0800 内存偏移[10303:272382] value = 4
//c的地址为7ffeef40c040
//c的四个元素分别为7ffeef40c040,7ffeef40c044,7ffeef40c048,7ffeef40c04c
- 数组的地址就是数组元素中的
首地址,即&c和&c[0]都是首地址 - 数组中每个元素之间的
地址间隔,根据当前元素的数据类型决定的 - 数组的元素地址可以通过
首地址+n*类型大小方式,这种方式是数组中的元素类型必须相同。 - 数组元素不相同用
首地址+偏移量方式,根据当前变量的偏移值(需要前面类型大小相加)
二,isa走位
- 通过之前的文章可以知道对象本质是
结构体,结构体的第一个成员变量就是isa。而且isa就指向当前的类,苹果为什么要这么做呢?下面我们来探究一下
1,苹果官方isa走位图
- 从图中我们大概能看出一些规律
- 非根类(NSObject)
对象的isa指向类,类指isa向元类,元类isa指向根元类,根元类isa指向自己 - 根类(NSObject)
对象的isa指向根类,根类isa指向根元类,根元类isa指向自己 - 非根类(NSObject)的
父类的元类就是元类的父类
2,类对象内存个数
Class class1 = [NBPerson class];
Class class2 = [NBPerson alloc].class;
Class class3 = object_getClass([NBPerson alloc]);
Class class4 = [NBPerson alloc].class;
NSLog(@"\n-%p-\n-%p-\n-%p-\n-%p-",class1,class2,class3,class4);
//输出结果
2021-07-27 10:19:47.940292+0800 内存偏移[21711:526731]
-0x1021b5520-
-0x1021b5520-
-0x1021b5520-
-0x1021b5520-
源码分析:类对象的地址都是一样的,内存中每一个类只有一块内存,和普通的对象内存则不是同一个
-
上篇文章拿到类的信息有三种,用掩码来获取是比较快的而且直接的。
-
从
objc4的源码可以拿到:
x86_64:define ISA_MASK 0x00007ffffffffff8ULL
arm64:define ISA_MASK 0x0000000ffffffff8ULL
arm64(simulators):define ISA_MASK 0x007ffffffffffff8ULL
3,元类探究
NBPerson *p1 = [[NBPerson alloc]init];
NSLog(@"%@",p1);
- 断点调试
2021-07-27 10:29:03.000337+0800 内存偏移[21861:534807] <NBPerson: 0x6000009e0230>
(lldb) p/x p1
(NBPerson *) $0 = 0x00006000009e0230
(lldb) x/4gx 0x00006000009e0230//对象地址
0x6000009e0230: 0x000000010c315520 0x0000000000000000
0x6000009e0240: 0x00001759aa2a0240 0x0000600000bc0026
//通过对象的isa找到类地址
(lldb) p/x 0x000000010c315520 & 0x007ffffffffffff8
(long) $1 = 0x000000010c315520
(lldb) po 0x000000010c315520
NBPerson
//通过类的isa找到元类地址
(lldb) x/4gx 0x000000010c315520
0x10c315520: 0x000000010c3154f8 0x00007fff86d50660
0x10c315530: 0x00006000037ed200 0x000580100000000f
(lldb) p/x 0x000000010c3154f8 & 0x007ffffffffffff8
(long) $9 = 0x000000010c3154f8
(lldb) po 0x000000010c3154f8
NBPerson
- 断点流程总结
- 通过对象
p1的isa0x000000010c315520找到 类的地址0x000000010c315520 - 通过类
NBPerson的isa0x000000010c3154f8找到元类的地址0x000000010c3154f8
4,根元类引出
- 继续上一步断点
//元类的isa找到根元类
(lldb) x/4gx 0x000000010c3154f8
0x10c3154f8: 0x00007fff86d50638 0x00007fff86d50638
0x10c315508: 0x00006000025e0700 0x0004c03100000007
(lldb) p/x 0x00007fff86d50638 & 0x007ffffffffffff8
(long) $11 = 0x00007fff86d50638
(lldb) po 0x00007fff86d50638
NSObject
//根元类的isa找到根根元类(其实就是自己)
(lldb) x/4gx 0x00007fff86d50638
0x7fff86d50638: 0x00007fff86d50638 0x00007fff86d50660
0x7fff86d50648: 0x00006000037e8400 0x0009c0310000000f
(lldb) p/x 0x00007fff86d50638 & 0x007ffffffffffff8
(long) $13 = 0x00007fff86d50638
(lldb) po 0x00007fff86d50638
NSObject
- 断点流程总结
元类的isa指向根元类- 根元类的isa指向
自己
元类和根元类的探究刚好和isa走位图中 objc(对象) --> class(类) --> metaClass(元类) --> rootMetaClass(根元类) --> rootMetaClass(根元类自己)。
5,类继承
Class tMetaClass = object_getClass(NBTeacher.class);//NBTeacher的元类
Class tMetaSuperClass = class_getSuperclass(tMetaClass);//NBTeacher的元类的父类
Class pMetaClass = object_getClass(NBPerson.class); //NBPerson的元类
Class pMeatSuperClass = class_getSuperclass(pMetaClass);//NBPerson的元类的父类
Class nMetaClass = object_getClass(NSObject.class);//NSObject的元类
Class nSuperClass = class_getSuperclass(NSObject.class);//NSObject的父类
Class nMetaSuperClass = class_getSuperclass(nMetaClass);//NSObject的元类的父类
NSLog(@"NBTeacher-%p",NBTeacher.class);
NSLog(@"NBPerson-%p",NBPerson.class);
NSLog(@"NSObject-%p",NSObject.class);
NSLog(@"%@ - %p - %@ - %p",tMetaClass,tMetaClass,tMetaSuperClass,tMetaSuperClass);
NSLog(@"%@ - %p - %@ - %p",pMetaClass,pMetaClass,pMeatSuperClass,pMeatSuperClass);
NSLog(@"%@ - %p - %@ - %p",nMetaClass,nMetaClass,nMetaSuperClass,nMetaSuperClass);
NSLog(@"%@ - %p",nSuperClass,nSuperClass);
- 输出结果
2021-07-27 16:59:36.204223+0800 内存偏移[29797:756093] NBTeacher-0x10f015620
2021-07-27 16:59:36.204689+0800 内存偏移[29797:756093] NBPerson-0x10f0155d0
2021-07-27 16:59:36.204782+0800 内存偏移[29797:756093] NSObject-0x7fff86d50660
2021-07-27 16:59:36.204917+0800 内存偏移[29797:756093] NBTeacher - 0x10f0155f8 - NBPerson - 0x10f0155a8
2021-07-27 16:59:36.205001+0800 内存偏移[29797:756093] NBPerson - 0x10f0155a8 - NSObject - 0x7fff86d50638
2021-07-27 16:59:36.205082+0800 内存偏移[29797:756093] NSObject - 0x7fff86d50638 - NSObject - 0x7fff86d50660
2021-07-27 16:59:36.205176+0800 内存偏移[29797:756093] (null) - 0x0
NBTeacher元类的父类和NBPerson的元类是同一个NBPerson元类的父类和NSObject的元类是同一个NBTeacher继承NBPerson,NBPerson继承NSObject,NSObject的父类是nilNBTeacher元类 继承NBPerson元类,NBPerson继承根元类,根元类继承NSObject
三,类结构分析
查看objc4(818版本)的源码objc-runtime-new.h中,找到了objc_class结构如下:
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags
Class getSuperclass() const {
#if __has_feature(ptrauth_calls)
# if ISA_SIGNING_AUTH_MODE == ISA_SIGNING_AUTH
if (superclass == Nil)
return Nil;
#if SUPERCLASS_SIGNING_TREAT_UNSIGNED_AS_NIL
void *stripped = ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
if ((void *)superclass == stripped) {
void *resigned = ptrauth_sign_unauthenticated(stripped, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
if ((void *)superclass != resigned)
return Nil;
}
#endif
void *result = ptrauth_auth_data((void *)superclass, ISA_SIGNING_KEY, ptrauth_blend_discriminator(&superclass, ISA_SIGNING_DISCRIMINATOR_CLASS_SUPERCLASS));
return (Class)result;
# else
return (Class)ptrauth_strip((void *)superclass, ISA_SIGNING_KEY);
# endif
#else
return superclass;
#endif
}
- 源码分析
objc_class继承objc_object,objc_object里面只有一个成员变量isa
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
-
类的结构中也有隐藏的
isa,占用8个字节 -
Class superclass是类的父类,结构体指针占用8个字节 -
cache_t cache是类的缓存空间,占用16个字节 -
class_data_bits_t保存类的数据,如属性、方法等信息。
1,探究cache_t的大小
- 搜索struct cache_t找到
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask;//8字节
union {
struct {
explicit_atomic<mask_t> _maybeMask;//4字节
#if __LP64__
uint16_t _flags;//2字节 objc2走这个
#endif
uint16_t _occupied;//2字节
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;//8字节
};
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
// _bucketsAndMaybeMask is a buckets_t pointer
// _maybeMask is the buckets mask
static constexpr uintptr_t bucketsMask = ~0ul;
static_assert(!CONFIG_USE_PREOPT_CACHES, "preoptimized caches not supported");
// .........忽略与结构体内存无关的代码
//源码位置为objc-runtime-new.h文件第338行-550行
typedef unsigned long uintptr_t是无符号长整形,占用8个字节。preopt_cache_t *是结构体指针,占用8个字节。uint16_t是无符号16位整形,占用2个字节。- mask_t是
uint32_t类型的,占用4个字节。 - cache_t的内存大小为:
uintptr_t内存大小8个字节+union内存大小8个字节=16字节
所以
isa内存地址为首地址superclass地址为首地址+0x08cache_t地址为首地址+0x10bits地址为首地址+0x20
2,class_data_bits_t bits结构体
class_data_bits_t bits结构体记录的是类的属性、成员变量以及方法。所以必须要了解结构体里面有什么,查找class_data_bits
struct class_data_bits_t {
friend objc_class;
// Values are the FAST_ flags above.
uintptr_t bits;
private:
bool getBit(uintptr_t bit) const
{
return bits & bit;
}
// Atomically set the bits in `set` and clear the bits in `clear`.
// set and clear must not overlap.
void setAndClearBits(uintptr_t set, uintptr_t clear)
{
ASSERT((set & clear) == 0);
uintptr_t newBits, oldBits = LoadExclusive(&bits);
//此处省略部分代码
public:
class_rw_t* data() const {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
void setData(class_rw_t *newData)
{
//此处省略部分代码
- class_rw_t 源码
struct class_rw_t {
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint16_t witness;
#if SUPPORT_INDEXED_ISA
uint16_t index;
#endif
explicit_atomic<uintptr_t> ro_or_rw_ext;
Class firstSubclass;
Class nextSiblingClass;
private:
using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t, class_rw_ext_t, PTRAUTH_STR("class_ro_t"), PTRAUTH_STR("class_rw_ext_t")>;
const ro_or_rw_ext_t get_ro_or_rwe() const {
return ro_or_rw_ext_t{ro_or_rw_ext};
}
void set_ro_or_rwe(const class_ro_t *ro) {
ro_or_rw_ext_t{ro, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_relaxed);
}
void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) {
// the release barrier is so that the class_rw_ext_t::ro initialization
// is visible to lockless readers
rwe->ro = ro;
ro_or_rw_ext_t{rwe, &ro_or_rw_ext}.storeAt(ro_or_rw_ext, memory_order_release);
}
class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false);
public:
void setFlags(uint32_t set)
{
__c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED);
}
void clearFlags(uint32_t clear)
{
__c11_atomic_fetch_and((_Atomic(uint32_t) *)&flags, ~clear, __ATOMIC_RELAXED);
}
// set and clear must not overlap
void changeFlags(uint32_t set, uint32_t clear)
{
ASSERT((set & clear) == 0);
uint32_t oldf, newf;
do {
oldf = flags;
newf = (oldf | set) & ~clear;
} while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags));
}
class_rw_ext_t *ext() const {
return get_ro_or_rwe().dyn_cast<class_rw_ext_t *>(&ro_or_rw_ext);
}
class_rw_ext_t *extAllocIfNeeded() {
auto v = get_ro_or_rwe();
if (fastpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext);
} else {
return extAlloc(v.get<const class_ro_t *>(&ro_or_rw_ext));
}
}
class_rw_ext_t *deepCopy(const class_ro_t *ro) {
return extAlloc(ro, true);
}
const class_ro_t *ro() const {
auto v = get_ro_or_rwe();
if (slowpath(v.is<class_rw_ext_t *>())) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro;
}
return v.get<const class_ro_t *>(&ro_or_rw_ext);
}
void set_ro(const class_ro_t *ro) {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro;
} else {
set_ro_or_rwe(ro);
}
}
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
};
class_rw_t是结构体类型,提供了获取属性列表,方法列表,协议列表的方法。通过实例来验证下方法,属性,变量是不是在class_rw_t中,在类中添加属性和方法 以及成员变量,请继续往下看验证流程。
3,成员变量与类方法获取
- 新建NBPerson
@interface NBPerson : NSObject{
double height;
}
@property(nonatomic,copy)NSString *name;
@property(nonatomic,copy)NSString *nickName;
-(void)sayHello;
+(void)sayNB;
@end
@implementation NBPerson
-(void)sayHello{
NSLog(@"hello----");
}
+(void)sayNB{
NSLog(@"NB----");
}
@end
- 运行
NBPerson *nb = [NBPerson alloc];
nb.name = @"nb";
nb.nickName = @"nickeName";
- 断点调试
(lldb) p/x NBPerson.class //当前类
(Class) $4 = 0x0000000100008268 NBPerson
(lldb) p/x 0x0000000100008268 + 0x20 //bits地址
(long) $5 = 0x0000000100008288
(lldb) p (class_data_bits_t *)0x0000000100008288
(class_data_bits_t *) $6 = 0x0000000100008288
(lldb) p $6->data() //获取到class_ro_t
(class_rw_t *) $7 = 0x0000000101315fb0
(lldb) p $7->properties() //往下找
(const property_array_t) $8 = {
list_array_tt<property_t, property_list_t, RawPtr> = {
= {
list = {
ptr = 0x00000001000081e8
}
arrayAndFlag = 4295000552
}
}
}
(lldb) p $8.list
(const RawPtr<property_list_t>) $9 = {
ptr = 0x00000001000081e8
}
(lldb) p $9.ptr //找到属性列表地址
(property_list_t *const) $10 = 0x00000001000081e8
(lldb) p *$10 //获取属性列表数据
(property_list_t) $11 = {
entsize_list_tt<property_t, property_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 16, count = 2)
}
(lldb) p $11.get(0)//找到第一个属性
(property_t) $12 = (name = "name", attributes = "T@\"NSString\",C,N,V_name")
(lldb) p $11.get(1)//找到第二个属性
(property_t) $13 = (name = "nickName", attributes = "T@\"NSString\",C,N,V_nickName")
(lldb) p $11.get(2)//没有第三个属性数组越界
Assertion failed: (i < count), function get, file /Users/xiaozhongwang/Desktop/文件汇总/iOS/objc4_debug-master/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
-
能找到
name属性和nickname属性 -
没有找到
height成员变量 -
height跑哪里去了?接着找
-
class_rw_t中除了有属性,方法,协议以外,还有class_ro_t结构体指针类型的ro(),
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
union {
const uint8_t * ivarLayout;
Class nonMetaclass;
};
explicit_atomic<const char *> name;
// With ptrauth, this is signed if it points to a small list, but
// may be unsigned if it points to a big list.
void *baseMethodList;
protocol_list_t * baseProtocols;
const ivar_list_t * ivars;
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
- 接着上面的调试
lldb) p $7->ro()
(const class_ro_t *) $18 = 0x00000001000080a0
(lldb) p $18.ivars
(const ivar_list_t *const) $19 = 0x0000000100008180
Fix-it applied, fixed expression was:
$18->ivars
(lldb) p *$19
(const ivar_list_t) $20 = {
entsize_list_tt<ivar_t, ivar_list_t, 0, PointerModifierNop> = (entsizeAndFlags = 32, count = 3)
}
(lldb) p $20.get(0)
(ivar_t) $21 = {
offset = 0x0000000100008228
name = 0x0000000100003ecf "height"
type = 0x0000000100003f29 "d"
alignment_raw = 3
size = 8
}
(lldb) p $20.get(1)
(ivar_t) $22 = {
offset = 0x0000000100008230
name = 0x0000000100003ed6 "_name"
type = 0x0000000100003f2b "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $20.get(2)
(ivar_t) $23 = {
offset = 0x0000000100008238
name = 0x0000000100003edc "_nickName"
type = 0x0000000100003f2b "@\"NSString\""
alignment_raw = 3
size = 8
}
(lldb) p $20.get(3)//数组越界
Assertion failed: (i < count), function get, file /Users/xiaozhongwang/Desktop/文件汇总/iOS/objc4_debug-master/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
总结
-
查找到height成员变量和_name,_nickNmae成员变量
-
成员变量底层实现是
ivar_t,存储在class_ro_t成员变量列表 -
系统是自动给
属性添加_属性名的变量,存储在class_ro_t成员变量列表 -
方法探究
(lldb) p $7->methods()
(const method_array_t) $24 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x00000001000080e8
}
arrayAndFlag = 4295000296
}
}
}
(lldb) p $24.list
(const method_list_t_authed_ptr<method_list_t>) $25 = {
ptr = 0x00000001000080e8
}
(lldb) p $25.ptr
(method_list_t *const) $26 = 0x00000001000080e8
(lldb) p *$26
(method_list_t) $27 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 6)
}
(lldb) p $27.get(0).big()
(method_t::big) $28 = {
name = "sayHello"
types = 0x0000000100003f21 "v16@0:8"
imp = 0x0000000100003c90 (KCObjcBuild`-[NBPerson sayHello] at main.m:31)
}
(lldb) p $27.get(1).big()
(method_t::big) $29 = {
name = "name"
types = 0x0000000100003f37 "@16@0:8"
imp = 0x0000000100003cc0 (KCObjcBuild`-[NBPerson name] at main.m:20)
}
-
对象方法存储在NBPerson类中的方法列表method_list_t里 -
类方法没有在NBPerson的方法列表method_list_t里,类方法放在哪里呢?
发现使用get(index)的方式无法得到,使用get(index).big()才能获取,这是为什么呢?
探究property_t和method_t
struct property_t {
const char *name;
const char *attributes;
};
struct method_t {
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
big &big() const {
ASSERT(!isSmall());
return *(struct big *)this;
}
SEL name() const {
if (isSmall()) {
return (small().inSharedCache()
? (SEL)small().name.get()
: *(SEL *)small().name.get());
} else {
return big().name;
}
}
const char *types() const {
return isSmall() ? small().types.get() : big().types;
}
IMP imp(bool needsLock) const {
if (isSmall()) {
IMP imp = remappedImp(needsLock);
if (!imp)
imp = ptrauth_sign_unauthenticated(small().imp.get(),
ptrauth_key_function_pointer, 0);
return imp;
}
return big().imp;
}
属性底层实现是property_t,在property_t结构体中定义了name等变量方法底层实现是method_t,在method_t结构体中定义了一个big(),通过big()获取SEL和IMP
继续查找方法
(lldb) p $27.get(2).big()
(method_t::big) $31 = {
name = ".cxx_destruct"
types = 0x0000000100003f21 "v16@0:8"
imp = 0x0000000100003d80 (KCObjcBuild`-[NBPerson .cxx_destruct] at main.m:29)
}
(lldb) p $27.get(3).big()
(method_t::big) $32 = {
name = "setName:"
types = 0x0000000100003f3f "v24@0:8@16"
imp = 0x0000000100003cf0 (KCObjcBuild`-[NBPerson setName:] at main.m:20)
}
(lldb) p $27.get(4).big()
(method_t::big) $33 = {
name = "nickName"
types = 0x0000000100003f37 "@16@0:8"
imp = 0x0000000100003d20 (KCObjcBuild`-[NBPerson nickName] at main.m:21)
}
(lldb) p $27.get(5).big()
(method_t::big) $34 = {
name = "setNickName:"
types = 0x0000000100003f3f "v24@0:8@16"
imp = 0x0000000100003d50 (KCObjcBuild`-[NBPerson setNickName:] at main.m:21)
}
(lldb) p $27.get(6).big()
Assertion failed: (i < count), function get, file /Users/xiaozhongwang/Desktop/文件汇总/iOS/objc4_debug-master/objc4-818.2/runtime/objc-runtime-new.h, line 624.
error: Execution was interrupted, reason: signal SIGABRT.
The process has been returned to the state before expression evaluation.
(lldb)
method_list_t中有对象方法,属性的setter方法和getter方法method_list_t中没有获取到类方法
探究元类
(lldb) p/x object_getClass(NBPerson.class)
(Class) $0 = 0x0000000100008240
(lldb) p/x 0x0000000100008240 + 0x20
(long) $1 = 0x0000000100008260
(lldb) p (class_data_bits_t*)0x0000000100008260
(class_data_bits_t *) $2 = 0x0000000100008260
(lldb) p $2->data()
(class_rw_t *) $3 = 0x0000000100627d70
(lldb) p $3->methods()
(const method_array_t) $4 = {
list_array_tt<method_t, method_list_t, method_list_t_authed_ptr> = {
= {
list = {
ptr = 0x0000000100008080
}
arrayAndFlag = 4295000192
}
}
}
(lldb) p $4.list.ptr
(method_list_t *const) $5 = 0x0000000100008080
(lldb) p * $5
(method_list_t) $6 = {
entsize_list_tt<method_t, method_list_t, 4294901763, method_t::pointer_modifier> = (entsizeAndFlags = 27, count = 1)
}
(lldb) p $6.get(0).big()
(method_t::big) $7 = {
name = "sayNB"
types = 0x0000000100003f21 "v16@0:8"
imp = 0x0000000100003c60 (KCObjcBuild`+[NBPerson sayNB] at main.m:34)
}
-
在元类中找到
sayNB -
object_getClass获取到JCPerson的元类 -
元类中
method_list_t中存储着类方法
总结:
-
类的结构主要由
isa,superclass,cache,bits组成 -
bits中存储着属性,方法,协议 -
属性存储在property_list_t中,而成员变量存储在class_ro_t-->ivar_list_t,系统为属性自动生成的_属性名的变量也存储在class_ro_t-->ivar_list_t -
方法存储在method_list_t中,method_list_t主要存储着对象方法,属性的setter方法和getter方法,而类方法存储在元类中的method_list_t