深入理解Objective-C:Category

Category使用场景

Category是Objective-C 2.0之后添加的语言特性,Category的主要作用是为已经存在的类添加方法。除此之外,apple还推荐了Category的另外两个使用场景
可以把类的实现分开在几个不同的文件里面。

不过除了apple推荐的使用场景,广大开发者脑洞大开,还衍生出了category的其他几个
使用场景:

  • 模拟多继承
  • 把framework的私有方法公开

    Category好处

    这样做有几个显而易见的好处,
  • 可以减少单个文件的体积
  • 可以把不同的功能组织到不同的category里
  • 可以由多个开发者共同完成一个类
  • 可以按需加载想要的category 等等。
  • 声明私有方法

Category和Extension

Extension看起来很像一个匿名的Category,但是Extension和有名字的Category几乎完全是两个东西。 Extension在编译期决议,它就是类的一部分,在编译期和头文件里的
@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。

但是Category则完全不一样,它是在运行期决议的。
就Category和Extension的区别来看,我们可以推导出一个明显的事实,Extension可以添
加实例变量,而Category是无法添加实例变量的(因为在运行期,对象的内存布局已经确
定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。

挑灯细览-Category真面目

我们知道,所有的OC类和对象,在runtime层都是用struct表示的,Category也不例外,在
runtime层,Category用结构体Category_t(在objc-runtime-new.h中可以找到此定义),
它包含了
1)、类的名字(name)
2)、类(cls)
3)、Category中所有给类添加的实例方法的列表(instanceMethods)
4)、Category中所有添加的类方法的列表(classMethods)
5)、Category实现的所有协议的列表(protocols)
6)、Category中添加的所有属性(instanceProperties)

需要注意的有两点:
1)、Category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果Category和原来
类都有methodA,那么Category附加完成之后,类的方法列表里会有两个methodA
2)、Category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的
后面,这也就是我们平常所说的Category的方法会“覆盖”掉原来类的同名方法,这是因为
运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,
就会罢休,殊不知后面可能还有一样名字的方法。
系统在初始化runtime后,main函数启动前对其进行bind,此时会调用map_images方法
对其进行类结构初始化等处理,然后再调用在load_image方法调用所有的load方法,所以
在runtime调用项目中所有的load方法时,我们所有的类的结构已经
加载进内存并初始化了,此时在load方法中可以创建任何类的实例并给他们发送消息。

1.由于load方法在main函数前被调用,所以在load方法中尽量避免费时的操作,因为这些
操作都将导致程序启动时间的延长。
2.runtime中call_load_methods里是通过load方法的地址直接调用的load方法,而不是通
过消息机制来调用的。

Category Q & A

1)、在类的+load方法调用的时候,我们可以调用category中声明的方法么?
2)、这么些个+load方法,调用顺序是咋样的呢?

1)、可以调用,因为附加Category到类的工作会先于+load方法的执行
2)、+load的执行顺序是先类,后Category,而Category的+load执行顺序是根据编译顺序
决定的。
虽然对于+load的执行顺序是这样,但是对于“覆盖”掉的方法,则会先找到最后一个编译的
Category里的对应方法。