iOS面试题与核心基础之分类和类拓展

知识点

分类(Category)

分类的底层结构

struct category_t {
    const char *name;
    classref_t cls;
    struct method_list_t *instanceMethods;//实例方法列表
    struct method_list_t *classMethods;// 类方法列表
    struct protocol_list_t *protocols;//协议列表
    struct property_list_t *instanceProperties;// 实例属性列表
    
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeda) return classMethods;
        else return instanceMethods;
    }
    
    property_list_t *propertiesForMeta(bool isMeta) {
        if (isMeta) return nil;
        else return instanceProperties;
    }
}
  • 分类添加的方法可以『覆盖』原方法
  • 同名分类方法,谁生效取决于编译顺序
  • 名字相同的分类,编译报错

分类的加载处理过程

  • 编译的时候所有分类都变成单独的结构体
  • 程序开始运行时,通过Runtime加载某个类的所有Category数据
  • 把所有Category的方法、属性、协议数据,合并到一个大数组中
    后面参与编译的Category数据,会在数组的前面
  • 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面

类拓展(class extension)

注意:这里讨论的是Objective-C的类拓展(class extension), Swift的extension跟这个完全不同。

拓展的功能

  • 声明私有属性
  • 声明私有成员变量
  • 声明私有方法(意义不大,也许能辅助阅读)

拓展的特点

  • 编译时决定
  • 只以声明的形式存在,多数情况下寄生于宿主的.m文件中
  • 不能为系统类添加拓展

关联对象

默认情况下,因为分类底层结构的限制(没有常见对象编译后的结构体中用于存储实例变量的struct objc_ivar_list *ivars 数组),不能添加成员变量到分类中。但可以通过关联对象来间接实现

关联对象提供了以下API

/// 添加关联对象
void objc_setAssociatedObject(id object, const void * key,
                                id value, objc_AssociationPolicy policy)

/// 获得关联对象
id objc_getAssociatedObject(id object, const void * key)

/// 移除所有的关联对象
void objc_removeAssociatedObjects(id object)

原理

关联对象并不是存储在被关联对象本身内存中
关联对象存储在全局的统一的一个AssociationsManager中

实现关联对象技术的核心对象有

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

objc4源码解读:objc-references.mm

objc_AssociationPolicy

  • OBJC_ASSOCIATION_ASSIGN assign
  • OBJC_ASSOCIATION_RETAIN_NONATOMIC strong, nonatomic
  • OBJC_ASSOCIATION_COPY_NONATOMIC copy, nonaomic
  • OBJC_ASSOCIATION_RETAIN strong atomic
  • OBJC_ASSOCIATION_COPY copy atomic

key的常见用法

  • 使用属性名作为key
  • 使用get方法的@selecor作为key

面试题

  1. 在开发中,你用分类做了哪些事情?
  • 声明私有方法
  • 分解体积庞大的类文件
  • 将Framework的私有方法公开化
  1. 分类和类拓展(class extension)的区别?
    分类

    • 运行时决定
    • 可以为系统类添加分类
      拓展
    • 编译后即合并,即编译时决定
    • 不能为系统类添加拓展
  2. 分类中可以添加哪些内容?

  • 实例方法
  • 类方法
  • 协议
  • 属性(只有getter和setter,实例变量需要通过关联对象来实现)
  1. 为什么分类不能添加实例变量?
    clang编译之后,每一个分类都生成一个结构体,结构体中有类方法,实例方法,协议和属性等数组,没有常见对象编译后的结构体中用于存储实例变量的struct objc_ivar_list *ivars 数组

你可能感兴趣的:(iOS面试题与核心基础之分类和类拓展)