美文网首页
ios Category

ios Category

作者: 赵哥窟 | 来源:发表于2018-09-04 16:19 被阅读9次
    Category简介

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

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

    category的其他几个使用场景:

    • 模拟多继承
    • 把framework的私有方法公开
    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;
    

    从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代码如下:

    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方法

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

    相关文章

      网友评论

          本文标题:ios Category

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