美文网首页
ios 类的结构分析

ios 类的结构分析

作者: 瞬间完善 | 来源:发表于2019-12-23 13:19 被阅读0次

1、首先抛出问题   类的结构是什么?

首先创建一个LGPesron的类,去cpp文件中。

利用clang编译成cpp源码(先cd到当前文件的目录):

```

clang -rewrite-objc main.m -o main.cpp

存在UIKit等其他动态引用库时:

clang -rewrite-objc -fobjc-arc -fobjc-runtime=ios-13.0.0 -isysroot/Application/Xcode.app/Comtents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator13.0.sdk main.m

xcrun xcode 命令

模拟器:xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

真机:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

```

打开cpp文件中全局搜索LGPerson,可以看到

typedef struct objc_object LGPerson;

struct objc_class {

    Class_Nonnullisa__attribute__((deprecated));

}__attribute__((unavailable));

在搜索objc_class ,可以看到

typedef struct objc_class *Class;

以上可以看到Class的真正类型为objc_class。

通过上面的探索,我们可以看到类是一种结构体,来自于我们的底层编译  objc_class  *Class,类就是Class.  往下层走我们可以看到objc_class的结构体,这个结构体来自于一个继承objc_object,这个结构体里面我们可以看到类的几个属性:

// Class ISA; // 8

    Class superclass;// 8

    cache_t cache;    // 16 不是8        // formerly cache pointer and vtable

    class_data_bits_t bits;

2、我们通常都是用类定义一些东西:属性和方法,那这些属性和方法的定义在哪找到?怎么去找?

在上面我们已经知道了类里面有Class ISA; Class superclass; cache_t cache;class_data_bits_t bits;这几个属性。

首先我们知道isa的指针是关联对象和类,superClass指向继承类,那么类的成员能够存储的地方就只有cache和bits

先看一下cache的结构体定义(不是一个结构体指针,是一个结构体),其中 mask_t为固定的4字节类型的值,而bucket_t则是一个8字节的指针,都不能存放我们定义的属性值,所以可以排除cache,这里也看出 cache的内存大小只有4+4+8=16字节

structcache_t {

    structbucket_t*_buckets;// 8

    mask_t_mask;              // 4

    mask_t_occupied;          // 4

所以可以猜测类的方法和属性在bits里面,继续看源码:

structobjc_class :objc_object{

    // Class ISA;

    Classsuper class;

    cache_t cache;            // formerly cache pointer and vtable

    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() { 

        return bits.data();

    }

我们可以看到class_rw_t 点击去:

structclass_rw_t {

  // Be warned that Symbolication knows the layout of this structure.

    uint32_t flags;

    uint32_t version;

    constclass_ro_t *ro;

    method_array_t  methods;

    property_array_t properties;

    protocol_array_t  protocols;

    Class firstSubclass;

    Class nextSiblingClass;

    char *demangledName;

#if SUPPORT_INDEXED_ISA

    uint32_t index;

#endif

由上我们是不是感觉类的属性和方法存在class_rw_t里面了,我们验证一下。

首先创建一个类LGPerson,添加成员变量hobby和属性nickName.添加示例方法和类方法。

@interfaceLGPerson :NSObject{

    NSString*hobby;

}

@property (nonatomic, copy) NSString *nickName;

- (void)sayHello;

+ (void)sayHappy;

@end

main函数中

 LGPerson*person = [LGPersonalloc];

  NSLog(@"%@ - %p",person,pClass);

xcode控制台:

x/4gx 打印person.class的内容,但是除了第一以及第二的内存,是我们熟悉的isa以及superClass指针以外,第三块地址的内容我们完全不知晓,第四块地址直接就不存在

(lldb) x/4gx person.class

0x100002338: 0x001d800100002311 0x0000000100afe140

0x100002348: 0x0000000101239a70 0x0000000200000003

按照Class结构体的成员定义顺序,以及内存对齐原则,我们尝试用指针偏移的方法,来找到第四块地址bits的所在,并且看看bits存放的内容是什么

(lldb) p/x 0x100002358

(long) $1 = 0x0000000100002358

这样我们什么也看不出来,看源码,我们用class_data_bits_t强转

(lldb) p (class_data_bits_t *)0x0000000100002358

(class_data_bits_t *) $2 = 0x0000000100002358

打印出class_data_bits_t的结构体。看上面源码我们之后rw是返回的是bits.data(),我们也调用一下,看看输出啥

(lldb) p $2->data()

(class_rw_t *) $3 = 0x000000010100cbe0

(lldb) p *$3

(class_rw_t) $4 = {

  flags = 2148139008

  version = 0

  ro = 0x0000000100002288

  methods = {

    list_array_tt = {

       = {

        list = 0x00000001000021d8

        arrayAndFlag = 4294975960

      }

    }

  }

  properties = {

    list_array_tt = {

       = {

        list = 0x0000000100002270

        arrayAndFlag = 4294976112

      }

    }

  }

  protocols = {

    list_array_tt = {

       = {

        list = 0x0000000000000000

        arrayAndFlag = 0

      }

    }

  }

  firstSubclass = nil

  nextSiblingClass = NSDate

  demangledName = 0x0000000000000000

}

通过上面输出我们可看到rw里面的properties里面并没有存储内容,那我们的成员变量和属性存在哪呢?在源码中我们直接被  method_array_t  methods; property_array_t properties;protocol_array_t  protocols;吸引,是否忘记了一个constclass_ro_t *ro;点击去:

structclass_ro_t {

    uint32_tflags;

    uint32_tinstanceStart;

    uint32_tinstanceSize;

#ifdef __LP64__

    uint32_treserved;

#endif

    constuint8_t* ivarLayout;

    constchar* name;

    method_list_t* baseMethodList;

    protocol_list_t* baseProtocols;

    constivar_list_t* ivars;

    constuint8_t* weakIvarLayout;

    property_list_t*baseProperties;

    method_list_t*baseMethods()const{

        return baseMethodList;

    }

};

我们发现这里面也有baseMethodList,baseProtocols,还有ivars,按照上面控制台来一遍;

 x/4gx person.class

0x100002338: 0x001d800100002311 0x0000000100afe140

0x100002348: 0x0000000101239a70 0x0000000200000003

(lldb) p/x 0x100002358

(long) $12 = 0x0000000100002358

(lldb) p (class_data_bits_t *)0x0000000100002358

(class_data_bits_t *) $13 = 0x0000000100002358

(lldb) p $13->data()

(class_rw_t *) $14 = 0x000000010100cbe0

(lldb) p $14->ro

(const class_ro_t *) $16 = 0x0000000100002288

(lldb) p *$16

(const class_ro_t) $17 = {

  flags = 388

  instanceStart = 8

  instanceSize = 24

  reserved = 0

  ivarLayout = 0x0000000100001f8d "\x02"

  name = 0x0000000100001f84 "LGPerson"

  baseMethodList = 0x00000001000021d8

  baseProtocols = 0x0000000000000000

  ivars = 0x0000000100002228

  weakIvarLayout = 0x0000000000000000

  baseProperties = 0x0000000100002270

}

(lldb) p $17.baseProperties

(property_list_t *const) $18 = 0x0000000100002270

(lldb) p *$18

(property_list_t) $19 = {

  entsize_list_tt = {

    entsizeAndFlags = 16

    count = 1

    first = (name = "nickName", attributes = "T@"NSString",C,N,V_nickName")

  }

}

我们想要的nickName出来了,那hobby呢?hobby肯定没在baseProperties里面,这个里面count=1,只存了一个值。hobby是成员变量,根据ro里面的字面意思肯定在ivars里面,我们打印看一下:

(lldb) p $17.ivars

(const ivar_list_t *const) $20 = 0x0000000100002228

(lldb) p *$20

(const ivar_list_t) $21 = {

  entsize_list_tt = {

    entsizeAndFlags = 32

    count = 2

    first = {

      offset = 0x0000000100002308

      name = 0x0000000100001e98 "hobby"

      type = 0x0000000100001faa "@"NSString""

      alignment_raw = 3

      size = 8

    }

  }

}

hobby出来了,眼睛毒辣的朋友会看到这里的count=2了,那是不是还有一个东西存在这呢?成员变量和属性是有区别的,在类中,我们定义的属性编译器也会自动的生成一个成员变量_nickName,所以我们在打印:

(lldb) p $21.get(1)

(ivar_t) $22 = {

  offset = 0x0000000100002300

  name = 0x0000000100001e9e "_nickName"

  type = 0x0000000100001faa "@"NSString""

  alignment_raw = 3

  size = 8

}

属性和成员变量都知道存在那了,那方法呢?字面意思baseMethodList,是不是在这里面,我们验证:

(lldb) p *$6

(const class_ro_t) $7 = {

  flags = 388

  instanceStart = 8

  instanceSize = 24

  reserved = 0

  ivarLayout = 0x0000000100001f8d "\x02"

  name = 0x0000000100001f84 "LGPerson"

  baseMethodList = 0x0000000100002238

  baseProtocols = 0x0000000000000000

  ivars = 0x00000001000022a0

  weakIvarLayout = 0x0000000000000000

  baseProperties = 0x00000001000022e8

}

(lldb) p $7.baseMethodList

(method_list_t *const) $8 = 0x0000000100002238

(lldb) p *$8

(method_list_t) $9 = {

  entsize_list_tt = {

    entsizeAndFlags = 26

    count = 4

    first = {

      name = "sayHello"

      types = 0x0000000100001f8f "v16@0:8"

      imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)

    }

  }

}

sayHello出来了,但是count=4这是为什么?不是只有2个方法吗?我们打印:

(lldb) p $9.get(0)

(method_t) $10 = {

  name = "sayHello"

  types = 0x0000000100001f8f "v16@0:8"

  imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)

}

(lldb) p $9.get(1)

(method_t) $11 = {

  name = "nickName"

  types = 0x0000000100001f97 "@16@0:8"

  imp = 0x0000000100001bf0 (LGTest`-[LGPerson nickName] at LGPerson.h:17)

}

(lldb) p $9.get(2)

(method_t) $12 = {

  name = "setNickName:"

  types = 0x0000000100001f9f "v24@0:8@16"

  imp = 0x0000000100001c20 (LGTest`-[LGPerson setNickName:] at LGPerson.h:17)

}

(lldb) p $9.get(3)

(method_t) $13 = {

  name = ".cxx_destruct"

  types = 0x0000000100001f8f "v16@0:8"

  imp = 0x0000000100001c60 (LGTest`-[LGPerson .cxx_destruct] at LGPerson.m:11)

}

从以上结果看出,里面存了nickName的setter和getter方法,还有c++的析构函数destruct方法,怎么没有类方法+(void)sayHappy?我们都知道类方法是存在元类中的,我们去元类中找:

(lldb) x/4gx person

0x101840260: 0x001d8001000023b5 0x0000000000000000

0x101840270: 0x0000000000000000 0x0000000000000000

(lldb) p/x 0x001d8001000023b5 & 0x00007ffffffffff8

(long) $1 = 0x00000001000023b0

(lldb) x/4gx 0x00000001000023b0

0x1000023b0: 0x001d800100002389 0x0000000100afe140

0x1000023c0: 0x00000001003a0e80 0x0000000000000000

(lldb) p (class_data_bits_t *)0x1000023d0

(class_data_bits_t *) $2 = 0x00000001000023d0

(lldb) p $2->data()

(class_rw_t *) $3 = 0x00000001018408c0

(lldb) p $3.ro

(const class_ro_t *) $4 = 0x0000000100002300

  Fix-it applied, fixed expression was: 

    $3->ro

(lldb) p $3->ro

(const class_ro_t *) $5 = 0x0000000100002300

(lldb) p *$5

(const class_ro_t) $6 = {

  flags = 388

  instanceStart = 8

  instanceSize = 24

  reserved = 0

  ivarLayout = 0x0000000100001f8d "\x02"

  name = 0x0000000100001f84 "LGPerson"

  baseMethodList = 0x0000000100002238

  baseProtocols = 0x0000000000000000

  ivars = 0x00000001000022a0

  weakIvarLayout = 0x0000000000000000

  baseProperties = 0x00000001000022e8

}

(lldb) p $6.baseMethodList

(method_list_t *const) $7 = 0x0000000100002238

(lldb) p *$7

(method_list_t) $8 = {

  entsize_list_tt = {

    entsizeAndFlags = 26

    count = 4

    first = {

      name = "sayHello"

      types = 0x0000000100001f8f "v16@0:8"

      imp = 0x0000000100001b90 (LGTest`-[LGPerson sayHello] at LGPerson.m:13)

    }

  }

}

总结:

属性和成员变量存在ro的ivars里面,并且属性还存在ro的baseProperties中。

实例方法和类方法:我们自定义的实例方法和系统自动生成的setter、getter和c++的方法存在ro的baseMethodList,也就是类中。类方法存在元类中。

相关文章

  • iOS类结构:cache_t分析

    一、cache_t 内部结构分析 1.1 在iOS类的结构分析中,我们已经分析过类(Class)的本质是一个结构体...

  • iOS-底层分析之类的结构分析

    类的结构分析 本文主要分析iOS中的类以及类的结构,下面我们通过一个例子来探索类的结构 我们定义一个WPerson...

  • iOS 类的结构分析(下)

    在上一篇 iOS 类的结构分析(上) 分析了类的结构、isa 的走位以及类的内存分布(属性列表&实例方法列表),这...

  • iOS底层之cache_t探究

    我们在iOS底层之类的结构分析分析了类的内部结构,而类的C/C++底层实际是objc_class结构体,其中包含了...

  • [iOS] 类 & 类结构分析

    1. 类的分析 1.1 元类的引入 我们可能之前已经知道类其实也是一个对象,类的 isa 指针指向的是它的元类,...

  • iOS 类结构分析

    前言 通过本篇文章可以了解1.isa的走位2.类结构的分析3.什么是元类4.supclass的走位5.objc_c...

  • iOS类结构分析

    本文主要来探索一下iOS中类的结构,作为一个iOS开发者,我们有必要去了解关于类的底层知识。下面开始我们的探索。 ...

  • iOS - 类结构分析

    我们都知道,一个类可以创建多个不同实例对象,类自己也是对象(类对象),那么类在内存中会存在几份呢?看下面结果 得出...

  • iOS 类的结构分析

    在谈及面向对象编程的时候,总是离不开 对象 与 类 。对象 是对客观事物的抽象,类 是对 对象 的抽象。它们的关系...

  • iOS 类的结构分析

    1. 类的初探 在isa结构解析中,自定义LSPerson 类继承自NSObject,重写成C++代码如下 str...

网友评论

      本文标题:ios 类的结构分析

      本文链接:https://www.haomeiwen.com/subject/wqeznctx.html