洋仔的博客 洋仔的博客
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

洋仔

奋斗的小青年
首页
  • 个人心法总结

    • 价值心法
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • iOS基础知识
  • 前端
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 投资体系
  • 毛选
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 技术文档

  • GitHub技巧

  • Nodejs

  • 博客搭建

  • iOS基础知识

    • iOS底层相关

      • OC基础之常见知识
      • OC基础之属性关键字
      • OC基础之底层系列
      • OC反射机制系列
      • 分类与扩展
        • 分类(Category):
        • 关于类的 + (void)load 与 + (void)initialize 的区别
        • 类别与类扩展的区别:
      • load跟initialize
      • isa指针是什么
      • 引用计数管理
      • 字典系列
    • Runloop系列

    • Runtime系列

    • 内存管理系列

    • Block系列

    • 线程系列

    • KVC跟KVO系列以及通知中心

    • UI系列

    • 离屏渲染系列

    • 组件化系列跟架构

    • OC跟webview交互系列

    • 持久化系列

    • APP编译系列

    • APP性能优化系列

    • cocoapods系列

    • swift系列

    • Git系列

    • 网络相关

    • 三方库系列

    • 系统原理

    • 总结系列

    • 算法系列

    • 数据结构系列

  • 前端

  • 技术
  • iOS基础知识
  • iOS底层相关
洋仔
2023-09-24
目录

分类与扩展

# 分类(Category):

分类中的可以写@property, 但不会生成setter/getter方法, 也不会生成实现以及私有的成员变量(编译时会报警告);

可以在分类中访问原有类中.h中的属性;

如果分类中有和原有类同名的方法, 会优先调用分类中的方法, 就是说会忽略原有类的方法。所以同名方法调用的优先级为 分类 > 本类 > 父类。因此在开发中尽量不要覆盖原有类;

如果多个分类中都有和原有类中同名的方法, 那么调用该方法的时候执行谁由编译器决定;编译器会执行最后一个参与编译的分类中的方法。

// 定义在objc-runtime-new.h文件中
struct category_t {
    const char *name; // 比如给Student添加分类,name就是Student的类名
    classref_t cls;
    struct method_list_t *instanceMethods; // 分类的实例方法列表
    struct method_list_t *classMethods; // 分类的类方法列表
    struct protocol_list_t *protocols; // 分类的协议列表
    struct property_list_t *instanceProperties; // 分类的实例属性列表
    struct property_list_t *_classProperties; // 分类的类属性列表
};
1
2
3
4
5
6
7
8
9
10

# 关于类的 + (void)load 与 + (void)initialize 的区别

相同点

  • 两个函数都是系统自动调用,因此无需手动调用(如果手动调用则与普通函数调用类似);
  • 两个函数都会隐式调用各自父类对应的 + (void)load 或 + (void)initialize 方法,即子类调用方法之前,会优先调用其父类对应的方法;
  • 两个函数内部都使用了锁,因此两个函数都是线程安全的;

不同点

  • 调用时机不同:+ (void)load 在 main 函数之前执行,即 objc_init Runtime初始化时调用,且只会调用一次。+ (void)initialize 在类的方法首次被调用时执行,每个类只会调用一次,但父类可能会调用多次;

  • 调用方式不同:+ (void)load 是根据函数地址直接调用,+ (void)initialize 是通过消息发送机制即 objc_msgSend(id self, SEL _cmd, ...) 调用;

  • 子类父类调用关系不同:

    • 如果子类没有实现 + (void)load,则不会调用其父类的 + (void)load 方法。
    • 如果子类没有实现 + (void)initialize,则会调用其父类的方法,因此父类的 + (void)initialize 可能会调用多次;
  • 类别 category 对调用的影响不同:

    • 如果 category 中实现了 + (void)load,则会优先调用原类的的 + (void)load,再调用 category 的,即优先级为:父类 > 原类 > category

      • 没有继承关系的不同类中的 + (void)load 的调用顺序跟 Compile Sources 顺序有关,即在前面的优先编译的类或者 category 先调用( 备注: 所有类的 + (void)load 优先级大于 category 的优先级);

      • 同一个类的 category 的 + (void)load 的调用顺序跟 Compile Sources 顺序有关,即在前面的优先编译的 category 会先调用;

      • 同一镜像中主工程的 + (void)load 方法优先调用,然后再调用静态库的 + (void)load 方法。有多个静态库时,静态库之间的执行顺序与编译顺序有关,即它们在 Link Binary With Libraries 中的顺序;

      根据runtime的消息传递机制中的核心函数void objc_msgSend(id self,SEL cmd,...)来发送消息,先从当前类中查找调用的方法,若没有找到则继续从其父类中一层层往上找,那么对于category重写同一个方法,则在消息传递的过程中,会最先找到category中的方法并执行该方法。对于多个分类调用同一个方法,Xcode在运行时是根据buildPhases->Compile Sources里面的从上至下顺序编译的,编译时通过压栈的方式将多个分类压栈,根据后进先出的原则,后编译的会被先调用,(插入顶部添加,即[methodLists insertObject:category_method atIndex:0]; 所以objc_msgSend遍历方法列表查找SEL 对应的IMP时,会先找到分类重写的那个,调用执行)当objc_msgSend找到方法并调用之后,就不再继续传递消息,所以形成所谓的覆盖。

      • 不同镜像中,动态库的 + (void)load 方法优先调用,然后再调用主工程的 + (void)load,多个动态库的 + (void)load 方法的调用顺序跟编译顺序有关,即它们在 Link Binary With Libraries 中的顺序;
  • 如果 category 中实现了 + (void)initialize,则原类的 + (void)initialize 将不会再调用

    多个 category 中同时实现了 + (void)initialize 方法时,Compile Sources中顺序最下面的一个,即最后一个被编译 Category 的 + (void)initialize 会执行;

# 类别与类扩展的区别:

类别中原则上只能增加方法(能添加属性的的原因只是通过runtime解决无setter/getter的问题而已); 类扩展不仅可以增加方法,还可以增加实例变量(或者属性),只是该实例变量默认是@private类型的( 用范围只能在自身类,而不是子类或其他地方);

类扩展中声明的方法没被实现,编译器会报警,但是类别中的方法没被实现编译器是不会有任何警告的。这是因为类扩展是在编译阶段被添加到类中,而类别是在运行时添加到类中。

类扩展不能像类别那样拥有独立的实现部分(@implementation部分),也就是说,类扩展所声明的方法必须依托对应类的实现部分来实现。

定义在 .m 文件中的类扩展方法为私有的,定义在 .h 文件(头文件)中的类扩展方法为公有的。类扩展是在 .m 文件中声明私有方法的非常好的方式。

编辑 (opens new window)
上次更新: 2024/10/23, 23:26:17
OC反射机制系列
load跟initialize

← OC反射机制系列 load跟initialize→

最近更新
01
数组
10-25
02
数组双指针系列之对撞指针
10-25
03
数组双指针系列之快慢指针
10-25
更多文章>
Theme by Vdoing | Copyright © 2019-2024 Evan Xu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式