阅读 374

iOS 对象本质探索

前言

众所周知,OC语言是一门对C语言高度封装的面向对象语言,在我们的开发过程中,我们可以不断的去创建一个个类,然后声明一个个的对象来使用。然而对于对象在底层是如何体现的,我们平时却很少关注,那么今天就跟我一块儿去探索一下对象在底层是如何体现的吧!

第一步:探索的方法

1.Clang简介

呐尼?一天天的奇奇怪怪的,Clang是什么呢?我们可以通过下图大概了解一下,如果想要深入了解Clang我们可以去找谷歌或度娘,我们在这篇文章不做深入套路,该死的输入法,是不做深入讨论;

image.gif

这是Clang的一个很短的简介,那么到底如何实用呢?各位看官往下走;

image.gif

2.通过Clang命令来将OC文件重新编译成C++文件

首先我们可以通过终端,cd到我们的文件所在位置,然后通过clang -rewrite-objc main.m -o main.cpp这一条命令,将main.m文件转换成c++文件; 例如:

image.gif

在转换以前,我们的这个目录下只有一个main.m文件。当我们执行了clang那条命令以后我们会得到一个main.cpp文件;如下

image.gif

第二步:研究对象在底层是长什么样子?

首先我们在main.m文件中我们创建了一个LGPreson的类;

image.gif

然后我们打开通过执行clang命令得到的cpp文件;输入LGPreson我们可以找到一下代码:

image.gif

通过这张图片上的代码,我们可以发现oc中的对象在底层是以结构体的方式存在的;此时你是否会有疑问,我们通常创建的类中,是有属性存在的呀,那么属性在这里边是如何存在的呢?

image.gif

我们在这里边声明了一个personName的属性,我们在重新执行clang命令:

image.gif 从这我们可以看出来,在一个类中声明的属性,在底层中也是作为一个结构体的属性存在的;

如果我们注意观察,在LGPerson_IMPL的这个结构体中,我们除了可以看到我们自己声明的属性_personName外,我们还可以看到另外一个属性NSObject_IVARS,那么它又是什么呢,通过搜索NSObject_IMPL 我们可以看到一下代码:

image.gif

从这段代码中我们可以看出来,这个结构体里边存放就是我们经常挂在嘴边上的isa;是不是很惊讶,鼎鼎大名的isa竟然在这?从这里我们可以看出来,isa为Class类型, 然后Class又是什么呢?我们继续在输入框中输入Class,一个一个往下找,知道我们在某一个角落找到了下边的代码:

image.gif

知道我们看到了他,一个Class类型的指针,和一个id类型的指针,他们的类型都是objc_class,曾几何是我们还在想,id类型多么牛叉呀,别的对象声明前边都需要一个*号打头儿,但是id类型的却不需要,当你看到了两行底层代码的时候是否有种被欺骗了的感觉,原来id本身就是指针类型!同时是不是也意味深长的点了点头说了一句“so dei 斯 内”;

总结:

因此我们可以得到一个结论,oc中的对象在底层是以结构体的形式存在,在每一个对象的结构体当中都有一个继承父类结构体的NSObjct_IVARS,这个属性就是oc对象的isa,也是该对象分配内存时候的起始位置!

知识扩展:结构体,位域,联合体的关系

首先我们看一段代码如下:

image.gif

我们可以看到Car这个结构体有四个属性,都是BOOL类型,BOOL类型在分配内存的时候占一个字节,这是我们可以得到Car这个结构体所占内存为4个字节,同时我们也知道每个字节占8位,4个字节一共32位(0000 0000 0000 0000 0000 0000 0000 0000);但是如果我们仔细观察一下会发现,BOOL类型只有两种状态YES和NO,一个BOOL类型使用一位就可以实现他的作用,所以这四个属性,我们可以用4位二进制类表示0X 1111;如果我们按照4个字节来分配,这就会浪费很多的内存;由于内存最少有一个字节,所以我们可以给这个结构体分配一个字节,而不是4个字节,那我们如何将这个结构的内存控制在一个字节呢?于是就出现了位域的这个概念;

通过位域控制如下,在每个属性后边添加 “:”和位数(该属性占多少位):

image.gif

此时如果我们通过sizeof(car)来打印的话我们会得到一个1,这样我们可以大大的优化了内存;

我们在来看另外一段代码:

image.gif

在这段代码中我们可以看到我们有一个LGTeacher1的结构体,并且我们声明了一个teacher1对象,并给这个对象的name,age分别赋了值,此时我们通过p teacher1命令可以查看到,该对象的name,和age已经有了我们之前给他赋的值;

我们在看另外一段代码:

image.gif

从这段代码中我们可以看到,有一个LGTeacher2的联合体,我们声明了一个teacher2的对象,并给这个对象的name,age分别赋了值,此时我们通过p teacher2命令可以查看到,当代码走到teacher2.name = “Cooci”的时候,我们通过p teacher2可以看到此时name已经赋值成功,但是当我们继续执行完teacher2.age = 18的时候我们在通过p teacher2来查看,我们会发现name的值已经不存在,而age的值为18,此时我们可以意识到,name和age共用同一片内存,他们之间是互斥关系;

由这两段代码我们可以得出如下结论:

结构体中的属性是共存关系,每一个属性都会拥有自己的内存,但是这样容易造成内存浪费的问题,虽然可以通过位域来适当的优化,但是不能根治; 联合体中的属性共用同一片内存,因此里边的属性是互斥关系,虽然节省了内存但是属性值无法共存;

文章分类
iOS
文章标签