美文网首页
iOS里面的Category

iOS里面的Category

作者: MichealXXX | 来源:发表于2020-01-07 16:57 被阅读0次
什么是category

Category可以说是我们在开发中经常用到的一种手法了,尤其是在对一些我们看不到源码的类中添加方法,这种情况经常用到项目的工具类开发中,那么它究竟是怎么一个东西呢?这一篇就来探究一下Category

Category是在Objective-C 2.0之后添加的语言特性,它的主要作用是为已经存在的类里面添加方法,我们可以利用这个特性把一个类的实现放在不同的文件中,缩小单个文件体积,多人开发一个类,按需加载不同的category等。

category与extension

很多人会混淆extensioncategoryextension看起来很像一个匿名的category,但是它们完全不是一个东西,我们先来看看什么是extension

extension被开发者称之为扩展、延展、匿名分类。extension看起来很像一个匿名的category,但是extensioncategory几乎完全是两个东西。和category不同的是extension不但可以声明方法,还可以声明属性、成员变量extension一般用于声明私有方法,私有属性,私有成员变量

下面就是一个extensionextension通常会写在.m文件中。

extension编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension

@interface ViewController ()

@end
category的结构体

在我之前学习block的时候会去到结构体层面去看它的实现原理,一切都很清晰明了,同样对于category来说我们去看看它的结构体是怎样的。

typedef 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;
} category_t;

这个看起来很熟悉,我们对比一下类的结构体是什么样的:

//Class也表示一个结构体指针的类型
typedef struct objc_class *Class;

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if !__OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;

category的定义也可以看出category的可以添加实例方法,类方法,甚至可以实现协议,添加属性和但是分类却没有objc_ivar_list,因此无法添加实例变量

分类可以添加属性?

分类当然可以添加属性,只不过我们一般都说分类不能直接添加属性,我们在分类中增加属性,只是在属性的列表里添加了一个属性,并没有生成对应的ivarXcode也会警告没有实现gettersetter方法,所以我们无法通过gettersetter方法操作属性。

但是我们可以使用关联对象在分类中动态的为一个类增加属性。

//这里用@selector(color)来用作 const void *key 的指针
- (UIColor *)color {
    return objc_getAssociatedObject(self, _cmd);
}

- (void)setColor:(UIColor *)color {
    objc_setAssociatedObject(self, @selector(color), color, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Category的真面目

我们使用clang的命令去看看category到底会变成什么:

//实例方法列表
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_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
//分类中定义的方法
{{(struct objc_selector *)"printName", "v16@0:8", (void *)_I_MyClass_MyAddition_printName}}
};

//属性列表
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_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
//分类中添加的属性
{{"name","T@\"NSString\",C,N"}}
};

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_MyClass;

static struct _category_t _OBJC_$_CATEGORY_MyClass_$_MyAddition __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"MyClass",
0, // &OBJC_CLASS_$_MyClass,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_MyClass_$_MyAddition,
};
static void OBJC_CATEGORY_SETUP_$_MyClass_$_MyAddition(void ) {
_OBJC_$_CATEGORY_MyClass_$_MyAddition.cls = &OBJC_CLASS_$_MyClass;
}
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CATEGORY_SETUP[] = {
(void *)&OBJC_CATEGORY_SETUP_$_MyClass_$_MyAddition,
};
static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {
&OBJC_CLASS_$_MyClass,
};
static struct _class_t *_OBJC_LABEL_NONLAZY_CLASS_$[] = {
&OBJC_CLASS_$_MyClass,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_MyClass_$_MyAddition,
};

我们的分类代码会被编译成以上的样子,在我们的项目运行的时候,运行时库的原理是会将分类中定义的实例方法、协议以及属性添加到类上,把分类的类方法和协议添加到类的metaclass

category与+load

在类和category中都可以有+load方法,我们在类的+load方法调用的时候,我们可以调用category中声明的方法么?

答:可以调用,因为附加category到类的工作会先于+load方法的执行。

这么些个+load方法,调用顺序是咋样的呢?

答:+load的执行顺序是先类,后categorycategory+load执行顺序是根据编译顺序决定的

参考文章:https://tech.meituan.com/2015/03/03/diveintocategory.html

相关文章

网友评论

      本文标题:iOS里面的Category

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