关于OC类结构的一点片面之见

2018-04-23  本文已影响0人  深海时代

初用简书,请多指教.

用了这么久的类和对象,还不知道它们的实现方法,就觉得很别扭,找了个时间初步的自己看了看.就说一下自己的思路.

先说我一开始就知道的:

我们知道类其实是一个结构体,大概是下面的样子:

网上扒下来的类结构图

struct bucket_t  {

    SEL  key;      //方法名称

    IMP imp;      //方法的实现,imp是一个函数指针类型

};

struct cache_t {

    struct bucket_t *buckets;    //缓存方法的哈希桶数组指针,桶的数量 = mask + 1

    int  mask;        //桶的数量 - 1

    int  occupied;  //桶中已经缓存的方法数量。

};

isa指针 指向本类类对象;

super指针 指向父类类对象;

版本名字内存大小不介绍;

关键下面四个结构体:  属性列表;  方法列表;  缓存列表;  协议列表;

当方法调用时发生如下的事件:

objc_msgSend(someObject,_cmd,parameter); //_cmd=@selector(messageName:)

for  ( someObject->isa.(cache||Method_list)) == _cmd ?return _imp;

messageName(someObject,_cmd,parameter);

someObject 是接收体 _cmd: 是选择子 parameter 是参数表;

但是问题来了,Ivar呢,Method是静态固定结构的呀,Ivar是实时会变的呀,

说Method保存在类结构里我是信的,但Ivar的值肯定是放对象里的,那问题来了,怎么证明呢?

下面的是我自己试验的:(就仅仅讨论变量机制,不是看Ivar的就可以到这里了)

定义两个不相干类

类OBJC_Sub 类OBJC_Sub2 建立类A的对象并为属性赋值 调用方法-更换isa指针-调用方法

这里两个类的方法是一样的,变量也是;

结果一样的

更换过类从属仍然得到相同的结果,可得:变量的值确实存在对象里,完毕,谢谢大家,散了散了.

才怪,IvarList到底放的是什么?放地址也不对,类和对象是一对多的关系,类里存放的信息是无法针对到某个对象的.受某个朋友启发,我做了如下操作:

我把_str和numberString的物理位置换了换

于是:

是不是很神奇

然后我就猜测,类结构中的IvarList是一个存放名字和序号的顺序表;对象里存放属性值的是一个顺序表;对象使用属性的时候,根据属性在顺序表中的位置去自身顺序表查值.

如上:类1的_str的索引是1,那么它对编译器的意义就是类的第二个属性,所以他的调用结果是@"1024";

而在类2里_str的索引变成了0,那么它对编译器的意义就变成了类的第一个属性,所以他访问到了numBerString的值 一串随机中文编码;

_str 这个是没有内存意义的,有内存意义的是他的属性序号,就是编译器记住的他的位置;

于是我按照我的猜想又做了一些验证:

类一的属性 类二属性物理倒序 并在类二里输出了属性列表 果然属性也出现了对应倒序 输出了一下两个类的属性

由此可以验证下图模型(其实单链表还是顺序表我很纠结的,因为功能几乎一样,不过这不是重点):

我们天赋画手从来都是忽略细节画灵魂的 设计谋杀图一图二的图三

说结论:

由IvarList到object的映射是通过索引来的,由此我们改变变量位置会影响他对变量地址的判断;

object里顺序排列着变量的地址,这也解释了变量未初始化时的指针保护是怎么实现的(放一个初始指针0x0);

成员属性位于变量末尾,这个的测试我就不多说了,随便一测就能测出来;

值得一提的是类1的变量必须多于类2,因为对象初始化的时候为变量开辟的内存是根据类1来的.如果类1的变量多,切换isa指针后对象变量冗余,OK我访问不到没关系.但是如果类1变量少,切换之后对象变量不足,就会出现野指针报错,虽然你可能不会访问那个野指针,可能ARC会检查这个吧;(野指针见图三)

本来想说说观察者模式来着,可是那个跟方法有关系,故不在讨论之列;

之后还会讨论一下OC 的 NSString、NSArray、NSDictionary,因为他们对象的isa并没有指向本类,这个后面再说;

到这里就完成了,感谢看完的你们.

上一篇下一篇

猜你喜欢

热点阅读