美文网首页
OC中的Category&Extension区别及其原理

OC中的Category&Extension区别及其原理

作者: 希尔罗斯沃德_董 | 来源:发表于2021-08-09 16:26 被阅读0次

Category&Extension的主要区别

Category和Extension都能对分类进行扩展,但是它们各自实现的作用却有所区别。

Category

Category又称为类别、类目、分类等,它的主要特点有:

  • 可以用来给类添加新的方法
  • 不能给类添加成员变量,但是可以通过runtime给分类添加关联对象
  • 分类中使用@property定义变量,只会生成getter、setter方法的声明,而不会生成方法的实现和带下划线的成员变量
Extension

Extension又称类扩展、延展,它跟Category比的主要特点有:

  • 可以给类添加成员属性,但是是私有变量
  • 可以给类添加方法,也是私有方法

它们为什么会有以上区别呢?接下来我们通过clang编译它们的源码,查看底层C++的实现逻辑,从底层来分析这个原因。

Category&Extension的底层原理

首先创建一个自定义类TestObject,分别在本类、Category和Extension声明名一个个属性name、ext_name和cate_name,分别声明和实现一个方法testMethod、ext_testMethod和cate_testMethod,demo如下:

demo.jpeg
属性property底层原理对比分析

我们先到main.cpp文件截取到属性property相关的代码分析。

  • 本类属性name和Extension属性ext_name相关代码
    在截取的过程中,发现本类和Extension的属性的代码是被编译在一起的,一次我们放一起分析,查看他们编译时的情况:
extern "C" unsigned long OBJC_IVAR_$_TestObject$_name;
extern "C" unsigned long OBJC_IVAR_$_TestObject$_ext_name;
struct TestObject_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
    NSString *_ext_name;
};

static struct /*_ivar_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count;
    struct _ivar_t ivar_list[2];
} _OBJC_$_INSTANCE_VARIABLES_TestObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_ivar_t),
    2,
    {{(unsigned long int *)&OBJC_IVAR_$_TestObject$_name, "_name", "@\"NSString\"", 3, 8},
     {(unsigned long int *)&OBJC_IVAR_$_TestObject$_ext_name, "_ext_name", "@\"NSString\"", 3, 8}}
};
  • Category属性相关代码
    这里截取了Category的属性cate_name相关的代码:
static struct /*_prop_list_t*/ {
    unsigned int entsize;  // sizeof(struct _prop_t)
    unsigned int count_of_properties;
    struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_TestObject_$_Cate __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_prop_t),
    1,
    {{"cate_name","T@\"NSString\",&,N"}}
};

分析:在这里我们发现Extension和本类的属性name、ext_name编译时的底层逻辑都是一样的,都能生成成员变量,而且Extension的成员变量跟本类的成员变量都是一同被编译到对象结构里面。而Category这里只有一个_prop_list_t结构体,没有生成相应的成员变量。
接下来我们在对比方法列表,看看属性的setter方法和getter方法情况。

接下来比较方法列表相关的代码。

  • 本类和Extension相关代码
    这里因为本类和Extension的方法列表是被编译在一起的,所以放在一起分析:
// @implementation TestObject
static void _I_TestObject_testMethod(TestObject * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_kz_91163dcd57j_zw_xyry904bc0000gn_T_main_e5ae60_mi_0);
}

static void _I_TestObject_ext_testMethod(TestObject * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_kz_91163dcd57j_zw_xyry904bc0000gn_T_main_e5ae60_mi_1);
}

static NSString * _I_TestObject_name(TestObject * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_TestObject$_name)); }
static void _I_TestObject_setName_(TestObject * self, SEL _cmd, NSString *name) { (*(NSString **)((char *)self + OBJC_IVAR_$_TestObject$_name)) = name; }

static NSString * _I_TestObject_ext_name(TestObject * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_TestObject$_ext_name)); }
static void _I_TestObject_setExt_name_(TestObject * self, SEL _cmd, NSString *ext_name) { (*(NSString **)((char *)self + OBJC_IVAR_$_TestObject$_ext_name)) = ext_name; }
// @end

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[10];
} _OBJC_$_INSTANCE_METHODS_TestObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    10,
    {{(struct objc_selector *)"testMethod", "v16@0:8", (void *)_I_TestObject_testMethod},
    {(struct objc_selector *)"ext_testMethod", "v16@0:8", (void *)_I_TestObject_ext_testMethod},
    {(struct objc_selector *)"name", "@16@0:8", (void *)_I_TestObject_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_TestObject_setName_},
    {(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_TestObject_ext_name},
    {(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_TestObject_setExt_name_},
    {(struct objc_selector *)"name", "@16@0:8", (void *)_I_TestObject_name},
    {(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_TestObject_setName_},
    {(struct objc_selector *)"ext_name", "@16@0:8", (void *)_I_TestObject_ext_name},
    {(struct objc_selector *)"setExt_name:", "v24@0:8@16", (void *)_I_TestObject_setExt_name_}}
};
  • Category方法列表相关代码
    这里截取了Category的方法cate_testMethod相关的代码:
// @interface TestObject (Cate)
// @property(nonatomic, strong) NSString *cate_name;
// - (void)cate_testMethod;
/* @end */

// @implementation TestObject (Cate)

static void _I_TestObject_Cate_cate_testMethod(TestObject * self, SEL _cmd) {
    NSLog((NSString *)&__NSConstantStringImpl__var_folders_kz_91163dcd57j_zw_xyry904bc0000gn_T_main_e5ae60_mi_2);
}
// @end

static struct /*_method_list_t*/ {
    unsigned int entsize;  // sizeof(struct _objc_method)
    unsigned int method_count;
    struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_TestObject_$_Cate __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    sizeof(_objc_method),
    1,
    {{(struct objc_selector *)"cate_testMethod", "v16@0:8", (void *)_I_TestObject_Cate_cate_testMethod}}
};

分析:首先我们看到类的方法列表里面不近包含本类的,也包含Extension的,但是确不包含Category的方法,而Category的方法在编译时是单独存放的。而且本类和Extension都能生成属性的setter和getter方法,而Category却没有。

Category&Extension使用场景

经过上面的分析可以发现Extension它的底层实现跟@interface并没有多大差别,同样能声明方法和属性,同样能生成成员变量,但是Extension并没有自己的实现,它的实现跟本类是共用的。所以他一般用作方法和属性的声明,特别是经常用作私有化声明的时候会用到;
而Category则不一样,Category虽然生成不了成员变量,但是可以通过关联对象和属性配合使用,达到跟类的属性一样的使用效果。而且Category有自己的实现,这一个特性让Category可以对类进行模块化,比如说当一个类的代码比较多、比较复杂时,这时候为了便于代码的阅读和维护,通常可以利用Category分成不同的模块,各模块分别定义自己的接口和实现,这样既不会让这个类的代码看起来臃肿,同时让外部在访问这个类相关接口时没有感觉到Category和类的区别。

总结

其实Extension在原理上它是类的一部分,在编译时期会被编译到类结构里面,属于在编译时期确定的结构。而Category则是针对运行时而设计的,它是在运行时才会被加载到类结构里面。Category的加载又分为懒加载非懒加载懒加载是在类第一次被访问的时候跟类一起被加载,非懒加载是在程序启动过程中就跟类一起被加载了。想了解更多的关于Category的加载可以参考OC类的加载流程

相关文章

  • OC中的Category&Extension区别及其原理

    Category&Extension的主要区别 Category和Extension都能对分类进行扩展,但是它们各...

  • 结合 category 工作原理分析 OC2.0 中的 runt

    结合 category 工作原理分析 OC2.0 中的 runtime 结合 category 工作原理分析 OC...

  • 底层7:block本质

    面试题:block的原理是怎样的?本质是什么? block是封装了函数调用及其函数调用环境的OC对象,本质就是oc...

  • Swift5.1 学习笔记(一)

    基础语法以及汇编原理 oc与swift汇编的区别 编译分为前端编译、后端编译。下面的图就描述了oc和swift编译...

  • 非常不错的些面试题

    1.struct和class的区别及其如何选择使用 个人感觉这个题也挺不错的.也挺考察基础的.oc中的类底层都...

  • iOS--OC底层原理文章汇总

    OC底层原理01—alloc + init + new原理OC底层原理02—内存对齐OC底层原理03— isa探究...

  • 按位或操作

    1. OC 和 Swift 中的区别 OC中定义Options Swift 中定义Options ** 在Swif...

  • OC阶乘计算

    OC中的阶乘算法,原理就是递归。在OC中也可以用c语言来实现。

  • OC底层原理七: malloc源码分析

    OC底层原理 学习大纲 在OC底层原理三:探索alloc (你好,alloc大佬 )中我们介绍了alloc的三大核...

  • OC 与 Swift

    OC对象的本质(上):OC对象的底层实现原理OC对象的本质(中):OC对象的种类OC对象的本质(下):详解isa&...

网友评论

      本文标题:OC中的Category&Extension区别及其原理

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