返回

深挖iOS底层:揭秘分类加载与关联对象

IOS

iOS底层探索之分类的加载与关联对象

前言

在上一篇关于类加载的文章中,我们探究了dyld在编译阶段如何通过map_images方法对类进行加载的流程。本篇,我们将继续深入iOS底层,了解当存在分类时,分类是如何加载并与关联对象建立联系的。

分类加载

当我们对一个已存在的类进行分类时,分类会被编译成一个新的Objective-C类,该类继承自原始类,并包含分类中定义的方法和属性。分类的加载过程与类加载类似,同样是由dyld在运行时动态加载。

当dyld加载一个分类时,它会执行以下步骤:

  1. 从分类的Mach-O文件中读取分类信息,包括分类名称、基类名称以及分类中定义的方法和属性。
  2. 在运行时创建一个新的Objective-C类,该类继承自分类的基类。
  3. 将分类中定义的方法和属性添加到新创建的类中。
  4. 将新创建的类添加到Objective-C类层次结构中。

分类加载完成后,就可以通过分类名称来访问分类中定义的方法和属性。

关联对象

关联对象是一种机制,允许我们向Objective-C对象添加任意数据。关联对象可以是任何类型的对象,包括基本类型(如整型、浮点型)、字符串、数组、字典等。

要向对象添加关联对象,可以使用objc_setAssociatedObject()函数。该函数接收三个参数:

  • 要添加关联对象的对象。
  • 一个关联键,用于标识关联对象。
  • 要关联的对象。

要检索关联对象,可以使用objc_getAssociatedObject()函数。该函数接收两个参数:

  • 要检索关联对象的对象。
  • 关联键。

分类中的关联对象

分类可以通过关联对象向其基类添加额外数据。例如,我们可以使用关联对象来存储分类中定义的属性值。

要向分类中的基类添加关联对象,可以使用以下步骤:

  1. 使用objc_setAssociatedObject()函数向基类添加关联对象,并将关联键设置为分类名称。
  2. 在分类中,使用objc_getAssociatedObject()函数来检索关联对象。

示例

让我们通过一个示例来说明分类加载和关联对象的使用。假设我们有一个名为Person的类,定义了name属性。我们创建一个名为Student的分类,继承自Person类,并定义了一个age属性。

在分类的实现文件中,我们可以使用关联对象来存储age属性的值:

@implementation Student

- (void)setAge:(NSInteger)age {
    objc_setAssociatedObject(self, @selector(age), @(age), OBJC_ASSOCIATION_RETAIN);
}

- (NSInteger)age {
    return [objc_getAssociatedObject(self, @selector(age)) integerValue];
}

@end

在main函数中,我们可以使用分类来创建Student对象并访问age属性:

int main() {
    Person *person = [[Person alloc] init];
    person.name = @"John Doe";
    
    Student *student = [[Student alloc] init];
    student.name = @"Jane Smith";
    student.age = 20;
    
    NSLog(@"Person: %@, Age: %ld", person.name, person.age);
    NSLog(@"Student: %@, Age: %ld", student.name, student.age);
    
    return 0;
}

输出结果为:

Person: John Doe, Age: 0
Student: Jane Smith, Age: 20

在这个示例中,我们使用了关联对象来存储分类中定义的age属性值。分类通过关联对象向其基类添加了额外数据,从而扩展了基类的功能。

结论

分类是iOS开发中广泛使用的特性,它允许我们向现有类添加新功能,而无需修改类本身。分类的加载过程类似于类加载,它们都由dyld在运行时动态加载。关联对象则是一种机制,允许我们向Objective-C对象添加任意数据,分类可以通过关联对象向其基类添加额外数据。通过理解分类的加载和关联对象的使用,我们可以编写出更强大、更灵活的iOS代码。