返回

深入剖析 iOS 底层之:类的加载过程(下)

IOS

分类

在研究对象、类的本质的时候,我们都用 clang 命令将 main.m 转换成 main.cpp 文件查看其本质,分类也不例外,我们使用相同的方法分析。首先我们定义一个分类:

@interface Person (Extension)
- (void)sayHello;
@end

然后我们通过 clang 命令将此文件转换成 C++ 文件:

struct objc_category_t {
  Class isa;
  Class _class;
  const char *name;
  struct method_list_t *instanceMethods;
  struct method_list_t *classMethods;
  struct protocol_list_t *protocols;
  struct property_list_t *instanceProperties;
};

从输出结果中我们可以看到,分类实际上也是一个类,它有一个 isa 指针,指向该分类的元类;有一个 _class 指针,指向该分类所属的类;有一个 name 字符串,表示分类的名称;有一个 instanceMethods 方法列表,表示该分类的实例方法;有一个 classMethods 方法列表,表示该分类的类方法;有一个 protocols 协议列表,表示该分类所遵循的协议;还有一个 instanceProperties 属性列表,表示该分类的实例属性。

分类加载过程

分类的加载过程如下:

  1. 当一个分类被使用时,系统会首先检查该分类是否已经被加载。如果没有被加载,则会调用 objc_loadClass() 函数加载该分类。
  2. objc_loadClass() 函数会首先检查该分类是否已经被注册。如果没有被注册,则会调用 objc_registerClassPair() 函数注册该分类。
  3. objc_registerClassPair() 函数会为该分类创建一个元类,并将其添加到元类列表中。
  4. 然后,objc_registerClassPair() 函数会为该分类创建一个类,并将其添加到类列表中。
  5. 最后,objc_loadClass() 函数会将该分类的元类和类返回给调用者。

内存管理与性能优化

分类的内存管理与性能优化与类的内存管理与性能优化基本相同。分类的实例对象也是在堆上分配的,分类的类对象也是在元空间中分配的。分类的实例对象与类的实例对象一样,都需要在使用后手动释放。分类的类对象与类的类对象一样,不需要手动释放。

为了优化分类的性能,我们可以使用以下策略:

  • 避免在分类中定义实例变量。实例变量会增加分类的大小,并可能导致性能下降。
  • 避免在分类中定义方法。方法也会增加分类的大小,并可能导致性能下降。
  • 尽量使用分类来扩展已经存在的类,而不是创建新的类。这可以减少代码的冗余,并提高性能。

总结

分类是一种强大的机制,可以用来扩展已经存在的类。分类的加载过程与类的加载过程基本相同。分类的内存管理与性能优化与类的内存管理与性能优化基本相同。为了优化分类的性能,我们可以使用以下策略:避免在分类中定义实例变量,避免在分类中定义方法,尽量使用分类来扩展已经存在的类,而不是创建新的类。