美文网首页
分类-Category

分类-Category

作者: 川少叶 | 来源:发表于2017-02-23 21:37 被阅读16次

    1 简介

    • 主要为类添加方法:不管系统的类还是自定义的类
    • 声明私有方法(扩展)
    • 分类是运行期决议的;扩展是编译期决议,需要源码才能在主类添加扩展;所以系统类是无法添加扩展
    • 分类的结构体,没有成员变量(ivar)列表,所以无法添加成员变量
    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;
    

    2 Category如何加载

    • 把Category的实例方法,协议以及属性添加到主类
    • 把Category的类方法和协议添加到类的metaClass

    添加方法的策略:
    是将Category和主类的方法拼接起来,放到方法列表饭(数组):先加载主类的方法,加载分类方法时,是先把分类方法放到新数组,再把主类的方法放到新数组后
    而查找方法也是按照顺序查找,所以会有Category覆盖主类方法的说法

    for (uint32_t m = 0;
                 (scanForCustomRR || scanForCustomAWZ)  &&  m < mlist->count;
                 m++)
            {
                SEL sel = method_list_nth(mlist, m)->name;
                if (scanForCustomRR  &&  isRRSelector(sel)) {
                    cls->setHasCustomRR();
                    scanForCustomRR = false;
                } else if (scanForCustomAWZ  &&  isAWZSelector(sel)) {
                    cls->setHasCustomAWZ();
                    scanForCustomAWZ = false;
                }
            }
    
            // Fill method list array
            newLists[newCount++] = mlist;
        .
        .
        .
    
        // Copy old methods to the method list array
        for (i = 0; i < oldCount; i++) {
            newLists[newCount++] = oldLists[i];
        }
    

    3 +load的加载顺序

    先调用主类的load方法,再调用Category;Category之间的顺序由编译顺序决定

    4 关联对象(AssociatedObject)

    #import "MyClass.h"
    
    @interface MyClass (Category1)
    
    @property(nonatomic,copy) NSString *name;
    
    @end
    
    #import "MyClass+Category1.h"
    #import <objc/runtime.h>
    
    @implementation MyClass (Category1)
    
    + (void)load
    {
        NSLog(@"%@",@"load in Category1");
    }
    
    - (void)setName:(NSString *)name
    {
        objc_setAssociatedObject(self,
                                 "name",
                                 name,
                                 OBJC_ASSOCIATION_COPY);
    }
    
    - (NSString*)name
    {
        NSString *nameObject = objc_getAssociatedObject(self, "name");
        return nameObject;
    }
    
    @end
    

    所有的关联对象都由AssociationsManager管理,通过Hash表管理,key是对象地址,value是Hash表,这个Hash表则保存了所有的KV对。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里面是由一个静态AssociationsHashMap来存储所有的关联对象的。这相当于把所有对象的关联对象都存在一个全局map里面。而map的的key是这个对象的指针地址(任意两个不同对象的指针地址一定是不同的),而这个map的value又是另外一个AssociationsHashMap,里面保存了关联对象的kv对。而在对象的销毁逻辑里面,见objc-runtime-new.mm:

    void *objc_destructInstance(id obj) 
    {
        if (obj) {
            Class isa_gen = _object_getClass(obj);
            class_t *isa = newcls(isa_gen);
    
            // Read all of the flags at once for performance.
            bool cxx = hasCxxStructors(isa);
            bool assoc = !UseGC && _class_instancesHaveAssociatedObjects(isa_gen);
    
            // This order is important.
            if (cxx) object_cxxDestruct(obj);
            if (assoc) _object_remove_assocations(obj);
    
            if (!UseGC) objc_clear_deallocating(obj);
        }
    
        return obj;
    }
    

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

    参考文章

    http://tech.meituan.com/DiveIntoCategory.html

    相关文章

      网友评论

          本文标题:分类-Category

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