关于OC类结构的一点片面之见
初用简书,请多指教.
用了这么久的类和对象,还不知道它们的实现方法,就觉得很别扭,找了个时间初步的自己看了看.就说一下自己的思路.
先说我一开始就知道的:
我们知道类其实是一个结构体,大概是下面的样子:
网上扒下来的类结构图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并没有指向本类,这个后面再说;
到这里就完成了,感谢看完的你们.