美文网首页
Category VS Extension 原理详解

Category VS Extension 原理详解

作者: iYeso | 来源:发表于2017-11-07 18:27 被阅读64次

    Category VS Extension 原理详解

    深入理解Objective-C:Category


    一: category

    categoryObjective-C 2.0之后添加的语言特性,别人口中的 分类、类别其实都是指的categorycategory的主要作用是为已经存在的类添加方法。除此之外,apple还推荐了category的另外两个使用场景。

    1.1: 什么是Category

    可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处。

    • 可以减少单个文件的体积
    • 可以把不同的功能组织到不同的category里
    • 可以由多个开发者共同完成一个类
    • 可以按需加载想要的category
    • 声明私有方法

    apple的SDK中就大面积的使用了category这一特性。比如UIKit中的UIView。apple把不同的功能API进行了分类,这些分类包括UIViewGeometry、UIViewHierarchy、UIViewRendering等。

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

    • 模拟多继承(另外可以模拟多继承的还有protocol)
    • 把framework的私有方法公开
    1.2: category特点
    • category只能给某个已有的类扩充方法,不能扩充成员变量。
    • category中也可以添加属性,只不过@property只会生成settergetter的声明,不会生成settergetter的实现以及成员变量。
    • 如果category中的方法和类中原有方法同名,运行时会优先调用category中的方法。也就是,category中的方法会覆盖掉类中原有的方法。所以开发中尽量保证不要让分类中的方法和原有类中的方法名相同。避免出现这种情况的解决方案是给分类的方法名统一添加前缀。比如category_
    • 如果多个category中存在同名的方法,运行时到底调用哪个方法由编译器决定,最后一个参与编译的方法会被调用。

    如下图,给UIView添加了两个categoryonetwo),并且给这两个分类都添加了名为log的方法:

    UIView+one UIView+two

    在viewController中引入这两个category的.h文件。调用log方法:

    调用category方法

    当编译顺序如下图所示时,调用UIView + one.m的log方法,如下图:

    编译顺序 调用结果
    UIView + one.m移动到UIView + two.m上面,调用UIView + two.m的log方法,如下图: 编译顺序 调用结果
    1.3: 调用优先级

    分类(category) > 本类 > 父类。即,优先调用cateory中的方法,然后调用本类方法,最后调用父类方法。

    注意:category是在运行时加载的,不是在编译时。

    1.4: 为什么category不能添加成员变量?

    Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。它的定义如下:

    typedef struct objc_class *Class;
    

    objc_class结构体的定义如下:

    struct objc_class {
        Class isa  OBJC_ISA_AVAILABILITY;
    #if !__OBJC2__
        Class super_class                       OBJC2_UNAVAILABLE;  // 父类
        const char *name                        OBJC2_UNAVAILABLE;  // 类名
        long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0
        long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识
        long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小
        struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表
        struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表
        struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存
        struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表
    #endif
    } OBJC2_UNAVAILABLE;
    

    在上面的objc_class结构体中,ivarsobjc_ivar_list(成员变量列表)指针;methodLists是指向objc_method_list指针的指针。在Runtime中,objc_class结构体大小是固定的,不可能往这个结构体中添加数据,只能修改。所以ivars指向的是一个固定区域,只能修改成员变量值,不能增加成员变量个数。methodList是一个二维数组,所以可以修改*methodLists的值来增加成员方法,虽没办法扩展methodLists指向的内存区域,却可以改变这个内存区域的值(存储的是指针)。因此,可以动态添加方法,不能添加成员变量。

    相关文章

      网友评论

          本文标题:Category VS Extension 原理详解

          本文链接:https://www.haomeiwen.com/subject/kcojmxtx.html