前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ios Category

ios Category

作者头像
赵哥窟
发布2018-09-13 11:42:49
4060
发布2018-09-13 11:42:49
举报
文章被收录于专栏:日常技术分享日常技术分享
Category简介

category是Objective-C 2.0之后添加的语言特性,category的主要作用是为已经存在的类添加方法。除此之外,apple还推荐了category的另外两个使用场景1

  • 可以把类的实现分开在几个不同的文件里面。这样做有几个显而易见的好处, a)可以减少单个文件的体积 b)可以把不同的功能组织到不同的category里 c)可以由多个开发者共同完成一个类 d)可以按需加载想要的category 等等。
  • 声明私有方法

category的其他几个使用场景:

  • 模拟多继承
  • 把framework的私有方法公开
Category真面目
代码语言:javascript
复制
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;

从category的定义也可以看出category可以添加实例方法,类方法,甚至可以实现协议,添加属性。无法添加实例变量。

过程: 1.在编译时期,会将分类中实现的方法生成一个结构体 method_list_t 、将声明的属性生成一个结构体 property_list_t ,然后通过这些结构体生成一个结构体 category_t 。 2.在运行时期,Runtime 会拿到编译时期我们保存下来的结构体 category_t 3.然后将结构体 category_t 中的实例方法列表、协议列表、属性列表添加到主类中 4.将结构体 category_t 中的类方法列表、协议列表添加到主类的 metaClass 中

需要注意的有两点: 1)、category的方法没有“完全替换掉”原来类已经有的方法,也就是说如果category和原来类都有methodA,那么category附加完成之后,类的方法列表里会有两个methodA 2)、category的方法被放到了新方法列表的前面,而原来类的方法被放到了新方法列表的后面,这也就是我们平常所说的category的方法会“覆盖”掉原来类的同名方法,这是因为运行时在查找方法的时候是顺着方法列表的顺序查找的,它只要一找到对应名字的方法,就会罢休,殊不知后面可能还有一样名字的方法。

Category 为什么不能添加实例变量

通过结构体 category_t ,我们就可以知道,在 Category 中我们可以增加实例方法、类方法、协议、属性。这里没有 objc_ivar_list 结构体,代表我们不可以在分类中添加实例变量。 因为在运行期,对象的内存布局已经确定,如果添加实例变量就会破坏类的内部布局,这个就是 Category 中不能添加实例变量的根本原因。

为什么使用Runtime又可以添加属性?

使用Runtime技术中的关联对象可以为类别添加属性。 先看AssociationsManager代码如下:

代码语言:javascript
复制
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;
        }
    };

其原因是:关联对象都由AssociationsManager管理,AssociationsManager里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址,而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的key/value对。

Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗? Category中有load方法,load方法在程序启动装载类信息的时候就会调用。load方法可以继承。调用子类的load方法之前,会先调用父类的load方法

代码语言:javascript
复制
void call_load_methods(void) {
    // 是否已经录入
    static bool loading = NO;
    // 是否有关联的 Category
    bool more_categories;
    loadMethodLock.assertLocked();
    // 由于 loading 是全局静态布尔量,如果已经录入方法则直接退出
    if (loading) return;
    loading = YES;
    // 声明一个 autoreleasePool 对象
    // 使用 push 操作其目的是为了创建一个新的 autoreleasePool 对象
    void *pool = objc_autoreleasePoolPush();
    do {
        // 重复调用 load 方法,直到没有
        while (loadable_classes_used > 0) {
            call_class_loads();    // 首先调用类的load方法
        }
        // 调用 Category 中的 load 方法
        more_categories = call_category_loads();
        // 继续调用,直到所有 Class 全部完成
    } while (loadable_classes_used > 0  ||  more_categories);
    // 将创建的 autoreleasePool 对象释放
    objc_autoreleasePoolPop(pool);
    // 更改全局标记,表示已经录入
    loading = NO;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.09.04 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Category简介
  • Category真面目
  • Category 为什么不能添加实例变量
  • 为什么使用Runtime又可以添加属性?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档