OC对象的本质

做过iOS开发的同学都应该知道我们平时编写的OC代码的底层实现都是通过C/C++实现的,所以OC的对象都是基于C/C++的数据结构实现的

oc代码的编译

1、那么OC的对象、类都是基于C/C++的什么数据结构实现的呢?

我们来建立一个项目将OC的代码转化到C/C++的代码来看一下

    1)建立一个简单的项目:

项目例子

    2)打开终端进入项目文件夹下执行命令:$ clang -rewrite-objc main.m -o main.cpp

主意一定要生成cpp文件,如果生成c文件的话会丢失很多东西的

但是这样生成的话main.cpp文件会比较大,所以建议用xcode的一个指令进行指定在什么架构下的生成文件:$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp

    3)打开main-arm64.cpp文件,搜索NSObject可以查看到

NSObject的底层实现(IMPL就是实现的意思)

所以可以看到OC的对象其实转为C/C++的结构体实现的,而结构体中只包含一个isa的指针变量,那么根据C语言的知识可以看到,isa的指针就是NSObject初始化对象的地址(其实NSObject的方法和其他的信息都存在其他的地方并不在对象的空间内,后边会说),那么从目前来看一个NSObject的对象所占用的空间就是一个指针所占的空间(64位下为8个字节,32位为4个字节)

!!!主意:其实8个字节是不严谨的,后边解释!!!

2、接下来我们创建两个NSObject的子类,Student和Person类,student继承person

新建的两个类

    2.1那么这两个类的存储空间是多少呢?

    我们知道一个对象其实就是一个C/C++的结构体,那么我们将这两个类和上边一样的命令进行转化一下发现:

两个自定义类对象向C/C++的转换


通过分析person实例化的对象的内存为:4 +8 =12

student实例化的对象的内存为:4 +12 =16

其实是不对的,这里会涉及内存对齐的概念,那么我们来看一下这两个对象到底占用了多少个字节呢?我们通过代码来看一下:

通过runtime函数获取类的实例化对象在内存中的大小

所以真实的占用内存的大小都是16,这是因为内存对齐的原因,对于内存对齐大家可以百度查看。你自己看一下NSObject的实例对象是多大,其实是8,所以也证明了对象的内存中只存在成员变量的值,不保存其他的信息

    2.2我们还可以对对象的占用内存细节借助xcode看一下:

    1)打上断点然后View->Memory如图:


    2)

        由此也能看出stu的实力化对象一共所占用16个字节,_age =06 00 00 00     _no =09 00 00 00

        所以再次证明:一个类的实例化对象的内存只保存属性值,不会包含其他信息

3、由上边的一系列操作可以看出一个类的实例化对象的内存只保存属性值,不会包含其他信息,但是其他的类的信息都保存在哪儿呢?而且上边还有一个细节那就是实例化对象的isa的值(指针)到底指向哪儿?

先说结论:Objective-C中的对象,简称OC对象,主要可以分为3种

             1)instance对象(实例对象)就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象,instance对象在内存中存储的信息包括、isa指针、其他成员变量

             2)class对象(类对象)每个类在内存中有且只有一个class对象,class对象在内存中存储的信息主要包含:isa指针、superclass指针、类的属性信息(@property)、类的对象方法信息(instance method)、类的协议信息(protocol)、类的成员变量信息(ivar)、......

             3)meta-class对象(元类对象)meta-class对象和class对象的内存结构是一样的,但是用途不一样。meta-class对象在内存中存储的信息主要包含:isa指针、superclass指针、类的类方法信息(class method)、......

    3.1我们先证明两个个细节:1)每个类的内存中有且只有一个class对象

证明class对象在内存中有且只有一个

                2)meta_class对象在内存中也是一个但和class对象不是一个内存哦,他们都是class 类型,所以他们的结构都是一样的。内存是一个可以分析出来,因为class的对象都是一个,而获取元类的对象都是从class对象获取的,所以肯定是一样的。

获取meta_class的方法:

        Class metaClass =object_getClass([NSObject class]);//获取类对象的类就是元类,但是不能[[NSObject class]class]这样获取,这样获取永远只是类对象!只能通过runtime的方法获取!

查看Class是否为meta-class:


检查某个class对象是否为元类

    3.2再来看一下各个对象的isa指针到底指向谁呢?

借用网上一张图:

oc对象的isa和superclass指针指向

由图看出来instance对象的isa指向class,class对象的isa指向meta_class。由此可见:oc的对象的方法调用就可以串起来了,例如之前的student的instance对象调用对象方法:

Student *stu =[[Student  alloc]init];

[stu  studentMethod];

stu的指针就是isa的指针,而isa指向student的class,那么其实就是student的class对象调用的studentMethod,所以就可以分析出一个类的对象方法信息其实放在class中的,分析一下:因为所有的实例化对象的方法都一样,没必要在每个对象中都开辟一份内存空间来放方法信息,因为这是对内存利用不科学的,所以放在class对象中是比较好的

接下来我们证明一下

    1)新建项目:(这里面的lldb指令我就不再解释了)

stu的isa的指针和stu的class的地址

很明显stu的isa的指针和stu的class对象的地址是不一样的,跟我们上边说的结论不一样,那是因为从isa指针到class的地址是要做一次位运算的,根据架构不同&的值一不一样:


如果想直接看到ISA_MASK的值的话,可以到apple官网下载runtime的源码查找,这里就不在寻找了,直接验证:

通过一次位运算之后很明显的结果是一样的,说明之前的结论是对的,我这儿是模拟器所以使用的是第二个ISA_MASK值,你如果使用真机测试的话那么请使用第一个ISA_MASK值进行运算。

接下来的Person和NSObject的验证可以自己进行。还有那幅图的superclass的指向都可以通过LLDB指令进行验证:接下来我们验证一下superclass的那条线的指向:


很明显指向跟上边的那个图是一样的

然后我们在去看一下calss对象里面都有什么,其实可以从之前从apple官网下载的代码可以找到的,这个不方便演示但是我们可以借助xcode来证明一下:我们来借助mj老师的文件MJClassInfo.h里面的mj_objc_class的结构体来将class的对象强转到结构体来窥探下class里面的东西:(这里面会涉及一点c++的知识,你只要知道上边的结论也就行了,证明作为了解):

Class和MetaClass对象强转换为结构体


personClassData的内部信息


studenClassData的信息

其他的信息自己可以看下就可以看出class和metaClass对象里面的信息了如图:

各个对象所储存的信息

(主意,在打印的属性列表里面只显示第一个属性,其他的属性需要操作指针位移运算来访问其他的属性,作为了解。还有元类对象虽然有属性列表,但是里面都是空的,因为它放在类对象里面的。还有在上边打印的里面method_list是个数组,调用的时候也是一个个往下寻找,但是如果经常被调用的时候那么这个方法会放入一个cache里面用于快速调用)

至此我们已经看到oc对象的本质了,但是我们前边有个不严谨的说法是:一个NSObject对象占用8个字节,32bit下占用4个字节。其实底层做了改变,其实是开辟了16个字节,但是实际使用的的确是8或者4个字节。怎么看一下呢?在源码里面可以看到这个函数:


所以其实是一个对象至少分配16个字节!但是实际使用为8个字节,也可以用runtime的一函数


实际占用的空间和开辟的空间


最后的最后留个问题:给NSObject增加一个对象方法,那么[Student test]和[NSObject test],通过类调用test方法会报错么???(主意分析上边网上的那张图和一个方法的调用顺序分析一下就知道了)

ps:这个文章其实是我的学习笔记了,在小码哥学习李明杰老师的网络授课做得笔记,所以用了好多mj老师的东西,再次声明一下,感谢mj老师在it界耕耘!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,678评论 0 9
  • iOS底层原理总结 - 探寻OC对象的本质 对小码哥底层班视频学习的总结与记录。面试题部分,通过对面试题的分析探索...
    xx_cc阅读 21,185评论 31 177
  • Objective-C语言是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这种动态语言的...
    有一种再见叫青春阅读 574评论 0 3
  • 本文基于objc4-709源码进行分析。关于源码编译:objc - 编译Runtime源码objc4-706 ob...
    WeiHing阅读 809评论 1 3
  • 1 感谢前天 “逝者不可追,来者犹可待。”前天已经过去,前天种种我们无力改变,但是我们必须心存感激:感谢父母给我们...
    Constance1993阅读 237评论 0 0