关于Category,Extension的一些理解

(一)Category

特点:

category只能给某个已有的类扩充方法,不能扩充成员变量。

category中也可以添加属性,只不过@property只会生成setter和getter的声明,不会生成setter和getter的实现以及成员变量。

如果category中的方法和类中原有方法同名,运行时会优先调用category中的方法。也就是,category中的方法会覆盖掉类中原有的方法。所以开发中尽量保证不要让分类中的方法和原有类中的方法名相同。避免出现这种情况的解决方案是给分类的方法名统一添加前缀。比如category_。

如果多个category中存在同名的方法,运行时到底调用哪个方法由编译器决定,最后一个参与编译的方法会被调用。这个要看在xcode配置中,他们的.m 是在前还是在后,放在上面的先编译,那么后面编译的category同名方法就会被调用。

结构:

Category不允许为已有的类添加新的成员变量,实际上允许添加属性的,同样可以使用@property,但是不会生成_变量(带下划线的成员变量),也不会生成添加属性的getter和setter方法,所以,尽管添加了属性,也无法使用点语法调用getter和setter方法。

我们看下category的结构:

typedef struct category_t {

const char *name;  //类的名字

classref_t cls;  //类

struct method_list_t *instanceMethods;  //category中所有给类添加的实例方法的列表

struct method_list_t *classMethods;  //category中所有添加的类方法的列表

struct protocol_list_t *protocols;  //category实现的所有协议的列表

struct property_list_t *instanceProperties;  //category中添加的所有属性

} category_t;

我们看到它是有instanceProperties这样一个数组的,那么为什么说无法添加属性,其实是添加后不会自动生成get,set方法而已,所以在使用 点语法的时候,回报找不到方法的错误提示。

因此我们需要使用runtime动态的给category添加对应的get,set方法。

添加属性的案例,比如给一个分类添加 blog这样一个属性:

@property (nonatomic, copy) NSString *blog;

- (NSString *)blog

{

    // 根据关联的key,获取关联的值。

    return objc_getAssociatedObject(self, key);

}

/**

blog的setter方法

*/

- (void)setBlog:(NSString *)blog

{

    // 第一个参数:给哪个对象添加关联

    // 第二个参数:关联的key,通过这个key获取

    // 第三个参数:关联的value

    // 第四个参数:关联的策略

    objc_setAssociatedObject(self, key, blog, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

另外需要注意的有两点:

1)、category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA。

2)、category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休,殊不知后面可能还有一样名字的方法。

(二)Extension

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

比如很的.m 文件里面会在implementation 上面加个

@interface viewModel() //这就是extension,可以添加属性, 方法一般用于私有方法,私有属性,成员变量

@implementation ViewModel

(三)category和extension的区别

就category和extension的区别来看,我们可以推导出一个明显的事实,extension可以添加实例变量,而category是无法添加实例变量的(因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这对编译型语言来说是灾难性的)。

extension在编译期决议,它就是类的一部分,但是category则完全不一样,它是在运行期决议的。extension在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它、extension伴随类的产生而产生,亦随之一起消亡。

extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension,除非创建子类再添加extension。而category不需要有类的源码,我们可以给系统提供的类添加category。

extension可以添加实例变量,而category不可以。

extension和category都可以添加属性,但是category的属性不能生成成员变量和getter、setter方法的实现。

补充:

那么category 中关联的属性是存放在哪里呢,又如何销毁:
从runtime的源码中可以看到,这里其实所有的关联对象都由AssociationsManager管理,AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。

销毁:runtime的销毁对象函数objc_destructInstance里面会判断这个对象有没有关联对象,如果有,会调用_object_remove_assocations做关联对象的清理工作。

AssociationsManager定义如下:

class AssociationsManager {

    static OSSpinLock _lock;

    static AssociationsHashMap *_map;              // associative references:  object pointer -> PtrPtrHashMap.public:

    AssociationsManager()  { OSSpinLockLock(&_lock); }

    ~AssociationsManager()  { OSSpinLockUnlock(&_lock); }

    AssociationsHashMap &associations() {

        if (_map == NULL)

            _map = new AssociationsHashMap();

        return *_map;}}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1、accept=“application/zip,application/x-zip,application/x...
    无名程序猿阅读 5,779评论 0 1
  • 每个月能完成两三本新书的阅读。书籍类别与历史、哲学、宗教、灵性成长为主。一是自然喜欢二是旨在洗心涤虑,提升智慧。阅...
    一箱子的云朵阅读 672评论 1 3
  • 随着市场的不断开放,未来的市场,将出现大量充满财富的机会,又将出现一大批亿万富翁,同时也出现很多人将面临倒下,绝大...
    公子晗晗阅读 273评论 0 0