IOS- 底层原理-类结构分析

如何打印类信息

通过lldb指令打印类信息

  • 通过isa指针地址 & ISA_MASK
    *NSObject提供的class API
  • runtime底层调用API
通过isa指针地址 & ISA_MASK

通过alloc->_objc_rootAlloc->callAlloc->_objc_rootAllocWithZone->_class_createInstanceFromZone->initInstanceIsa->initIsa->isa_t找到位域位置(目前是处于macOS,所以使用的是x86_64)

  • 移动端 :__arm64__
    • ISA_MASK:0x0000000ffffffff8ULL
  • mac OS __x86_64__
    • ISA_MASK:0x00007ffffffffff8ULL

输入x/4gx person1,打印对象的isa指针得到0x001d8001000021e5,通过0x001d8001000021e5 & 0x00007ffffffffff8ULL得到类的指针地址``0x00000001000021e0

指针地址

NSObject提供的class API

通过lldb命令输入p/x LGPerson.class,调用NSObject提供的class API,得出类的指针地址

指针地址

runtime底层调用的API

通过lldb命令输入p/x object_getClass(person1),调用runtime底层调用API,得出类的指针地址

指针地址

算法特殊性

算法特殊性

如图可以看出0x00000001000021e00x00000001000021b8,打印的值都是LGPerson,即使0x00000001000021b8 & 0x00007ffffffffff8ULL得出的指针地址还是一样的,所以打印的结果还是LGPerson,其实这里就是算法的特殊性(三位抹零),里面做了处理。

那么为什么0x00000001000021e00x00000001000021b8打印的结果都是LGPerson呢?
其实这个东西就是元类,它没有非常清晰的概念。它不能向类一样时刻查看。它是系统给予非常重要的东西。

  • 当前对象isa归属于类
  • 类是一个对象,它的归属是元类
  • 所有类方法的存储都存在元类里面

元类的创建和定义都是由编译器自动完成

NSObject的根元类

通过lldb命令不停爬,最终从元类来到NSObject。顺序就是isa 对象-> 类(LGPerson)->元类(LGPerson)-> NSObject(根元类)

NSObject的根元类

进行验证NSObject是内存的NSObject吗?
通过lldb输入p/x NSObject.class得到结果0x00000001003f0140可以看出和0x00000001003f00f0是不同的。

NSObject的根元类

由此我们考虑一个问题(面试题目)
请问类对象和类信息在内存里面存在几份?
其实类对象内存里面永远只存在一份

但是上图可以看出有两个NSObject,这又是为什么呢?
其实NSObject是根元类和LGPerson一个原理isa 对象-> 类(LGPerson)->元类(LGPerson)-> NSObject(根元类)

那就我们来验证一下NSObject到底有多少份

通过上面发现地址都一样可以验证类对象内存永远只存在一份

其实0x00000001003f0140打印出的NSObject是来自于NSObject的元类(根元类)
我们可以通过lldb命令继续验证,原理就是类的对象指向元类,如图可以发现打印结果是一样就此就验证了上面的说法。

NSObject元类

于此同时,我们在NSObject根元类的基础上继续x/4gx打印信息,并且&0x00007ffffffffff8ULL,可以发现它还是指向自己

NSObject根元类指向自己

由此可以总结出一幅图


非常著名的图
  • 1:实例对象的isa指向class(类),类的isa指向元类,元类的isa指向根元类
  • 3:RootClass是来自于NSObject的实例对象和NSObject的类,NSObject的类指向NSObject根元类根元类根元类还是指向自己(少了一步元类的指向)。
  • 2:在特殊情况下,如果是父类实例对象指向当前的指向当前的元类元类指向根元类根元类还是指向根元类
    image
image

LGTercher 继承于 LGPerson是继承关系 即继承关系来自于类
但是实例对象tercher和实例对象person没有继承关系 即实例对象没有继承关系
NSObject父类的继承关系来自于nil(找到NSObject的类型就结束了)
元类也存在继承,根元类指向NSObject,万物皆是由NSObject创建即万物皆对象

上下层的对接

搜索objc_object进入objc_object源码来自于OBJC_ISA_AVAILABILITY

objc_object源码

通过搜索struct objc_class {进入objc_class源码,OBJC2_UNAVAILABLE旧的同时已经被废除

image

  • 很多的类底层都来自于objc_class结构体,因此NSObject也是来自于objc_class
  • objc_object是我们当前的根对象,是一个结构体

不管是NSObject还是对象都有一个isaisa来自于objc_object,同时objc_class继承于objc_object所以也是有isa

image

面试问题
objc_object 和对象的关系?
所有的对象都是以objc_object为模版继承过来的

总结:所有的对象+类+元类 都有isa. 所有的对象来自于NSObject(OC), 真正到底层的话是objc_object(C ,C++),struct objc_object *class定义一个class类型(所有的class都是以objc_object为模版)

类的内存分布

image

如图可以看到//Class ISA的位置,这只是提示作用,而不是单纯的屏蔽isa,但是我们在打印中可以发现LGPerson是有isa(0x00000001000021b8),原因是objc_class继承于objc_object,因此也拥有了父类的isa

类结构源码

下图可以看出10同时赋值给了ab,其实就是一种copy值拷贝,但是ab的地址是不同的。

image

image

观察下图,可以发现指针地址内存地址都不同,p1和p2都创建了新的内存地址,并且他们都是来自于LGPerson创建的,但是他们指针地址不同,其实指向它们的是第二个指针地址指针的指针

image

image.png

内存偏移

有时我们取不到值的时候而是取到对象,现在我们可可以拿到首值通过偏移的方式获取值。

  • 因为栈是连续的,相差4个字节
  • 通过首值进行内存偏移取值
    内存偏移

    image

探索类对象

前面我们已经知道objc_class继承于objc_object但是我们怎么知道superclasscache占用多少字节呢?达到我们平移到bits的位置。
那么我们就需要知道isa+superclass+cache = bits

image

cache_t是一个结构体,结构体的指针是8字节,但是占用的大小是根据内部的属性进行判断。
点击跳转cache_t源码,静态和函数不存在结构体中,所以可以排除,那么需要计算只有四个位置

image

  • isa是占用8字节
  • superclass也是class类型,所以也是占用8字节
  • cache_t占用16字节

通过lldb命令,根据首地址并且平移32字节打印bits信息

bits数据信息

$1因为是指针类型,所以调用data()方法。同时这是781的最新改版,旧的版本有显示methdos properties等数据。

  • 指针和对象使用->
  • 结构体使用.符号

781源码如何打印methdos properties等信息

bits方法信息
  • 在bits信息的基础上,通过p $5.methods()命令中的methods方法是由class_rw_t提供的,返回的实际类型是method_array_t
  • 由于list类型是method_list_t,是一个指针,所以通过p *$7获取内存中的信息,也证明了bits中存储了method_list
  • p $8.get(1)p $8.get(2)打印的是属性的setget方法,由系统自动生成。p $8.get(2)oc封装于c++底层会默认添加
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,185评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,445评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,684评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,564评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,681评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,874评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,025评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,761评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,217评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,545评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,694评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,351评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,988评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,778评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,007评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,427评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,580评论 2 349