Category、load、initialize

根据runtime的消息发送机制,核心函数void objc_msgSend(void /* id self, SEL op, … */ )发送消息,先从当先类的方法列表中查找调用方法,如果没有找到,则会向上寻找其父类中的方法列表。
对于category重写了主类中的方法,
OBJCKaTeX parse error: Expected group after '_' at position 17: …CATEGORY_Preson_̲Test.cls = OBJC_CLASS$_Preson

定义_class_t类型的OBJC_CLASS_KaTeX parse error: Expected group after '_' at position 20: …son结构体,最后将_OBJC_̲CATEGORY_PresonKaTeX parse error: Expected group after '_' at position 24: …s指针指向OBJC_CLASS_̲_Preson结构体地址。可以看出,cls指针指向的应该是分类的主类类对象的地址。分类源码中将定义的对象方法、类方法、属性等都存放到category_t结构体中。

_getObjc2CategoryList函数获取到分类列表之后,进行遍历,获取其中的方法、协议、属性等。最终都会调用remethododizeClass(cls)函数,该函数接受了类对象cls和分类数组cats, 而attachCategories(cls,cats,ture)函数会根据方法列表、属性列表、协议列表、malloc分配内存,根据多少个分类以及每一块方法需要多少内存来分配相应的内存地址。之后从分类数组里面往三个数组里面存放分类数组里面存放的分类方法、属性以及协议放入对应的mlist、proplists、protolosts数组中,这三个数组存放着所有分类的方法、属性和协议。

然后通过class_rw_t中存放着类对象的方法、属性和协议等数据,rw结构体通过类对象的data方法获取,所以rw里面存放这类对象里面的数据。再通过rw调用方法列表、属性列表、协议列表的attachList函数,将所有分类的方法、属性、协议列表数组传进去,attachList函数内部:
array()->lists:类对象原来的方法列表、属性列表、协议列表。
addedLists:传入所有分类的方法列表、属性列表、协议列表。
attachLists函数中最重要的两个方法:memmove内存移动和memcpy内存拷贝。
在这里插入图片描述

在这里插入图片描述

经过memmove和memcpy分类的方法、属性、协议列表被放在了类对象中原本存储的方法、属性、协议列表前面。也就是说分类重写本类的方法不会覆盖本类的方法而是优先调用。
Category可以添加属性,但并不会自动生成成员变量及set/get方法,分类在运行时才会去加载,因此
分类中不可以添加成员变量


initialize是通过新消息发送机制调用的,消息发送机制通过isa指针找到对应的方法与实现,因此先找到分类方法中的实现,会优先调用分类方法中的实现;类在第一次接受到消息时,就会调用initialize,相当于第一次使用类的时候就会调用initialize方法。调用子类的initialize之前,会保证调用父类的initialize方法,如过之前已经调用过initialize,就不会调用initialize方法了。如果分类重写initialize方法时,就会优先调用分类的方法。调用顺序:
父类initialize方法 —若分类重写initialize /或者/ 子类的initialize 均只会在第一次调用类的时候调用,不会再重复调用,
若一个类的分类实现了initialize,则不会再调用主类的initialize方法
若子类实现initialize方法,调用之前若父类没有被调用过,会先去调用父类initialize ,若父类已调用过则不会调用父类的initialize
若子类没有实现initialize方法,第一次调用子类就会调用其父类的initialize,所以父类的initialize可能会调用多次
若是有多个category实现了initialize,则根据build phases -> compile source 从上到下的编译顺序,最下方的category为准,编译是通过压栈方式将category压栈,后进先出,后编译先调用

编译时会根据编译顺序,先编译的类就会优先调用load方法,在调用之前会优先调用父类的load,若分类中也有load会后于主类load方法调用,若多个category,多个category内部的load方法调用顺序则是根据buildPhases->compile sources的顺序,从上到下的编译顺序
Load则是直接拿load方法的内存地址直接调用,而不是通过消息发送机制调用。因此,分类中重写load方法,只会先于主类方法调用,不会覆盖,调用顺序:
父类load —子类load —分类load

根据runtime的消息传递机制中的核心函数void objc_msgSend(id self,SEL cmd,…)来发送消息,先从当前类中查找调用的方法,若没有找到则继续从其父类中一层层往上找,那么对于category重写同一个方法,则在消息传递的过程中,会最先找到category中的方法并执行该方法。对于多个分类调用同一个方法,Xcode在运行时是根据buildPhases->Compile Sources里面的从上至下顺序编译的,编译时通过压栈的方式将多个分类压栈,根据后进先出的原则,后编译的会被先调用,当objc_msgSend找到方法并调用之后,就不再继续传递消息,所以形成所谓上的覆盖。并不是后面创建的就一定被调用,得看创建之后其在buildPhases->Compile Sources里面的位置


可参考 https://www.jianshu.com/p/fa66c8be42a2

xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Preson+Test.m

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值