iOS原理(三)----CateGory

2018-12-27  本文已影响0人  会笑的Even

iOS原理(三)----CateGory

创建一个Animal,及其两个分类,并调用其eat,run,sleep方法.

@interface Animal : NSObject

- (void)eat;

@end

@implementation Animal

- (void)eat {
    NSLog(@"---eat---");
}

@end

@interface Animal (Run)

- (void)run;

@end

@implementation Animal (Run)

- (void)run {
     NSLog(@"---run---");
}

@end

@interface Animal (Sleep)

- (void)sleep;

@end

@implementation Animal (Sleep)

- (void)sleep {
    NSLog(@"---sleep---");
}

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Animal *ani = [[Animal alloc] init];
        [ani eat];
        [ani run];
        [ani sleep];
    }
    return 0;
}

用命令生成C++变异文件:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp;

每个分类都会转成一个_category_t类型的结构体,

struct _category_t {
    // 类名
    const char *name;
    // 类信息
    struct _class_t *cls;
    // 实例方法列表
    const struct _method_list_t *instance_methods;
    // 类方法列表
    const struct _method_list_t *class_methods;
    // 协议列表
    const struct _protocol_list_t *protocols;
    // 属性列表
    const struct _prop_list_t *properties;
};

static struct _category_t _OBJC_$_CATEGORY_NSObject_$_Run __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "NSObject",
    0, // &OBJC_CLASS_$_NSObject,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_Run,
    0,
    0,
    0,
};

static struct _category_t _OBJC_$_CATEGORY_NSObject_$_Sleep __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
    "NSObject",
    0, // &OBJC_CLASS_$_NSObject,
    (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_Sleep,
    0,
    0,
    0,
};

最后把该类的所有分类放在:

static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [2] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
    &_OBJC_$_CATEGORY_NSObject_$_Run,
    &_OBJC_$_CATEGORY_NSObject_$_Sleep,
};

到底何时将分类的信息添加到原本的类当中的呢?查看苹果源码片段如下:

static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

    // fixme rearrange to remove these intermediate allocations
    // 方法数组
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    // 属性数组
    property_list_t **proplists = (property_list_t **)
        malloc(cats->count * sizeof(*proplists));
    // 协议数组
    protocol_list_t **protolists = (protocol_list_t **)
        malloc(cats->count * sizeof(*protolists));

    // Count backwards through cats to get newest categories first
    int mcount = 0;
    int propcount = 0;
    int protocount = 0;
    int i = cats->count;
    bool fromBundle = NO;
    // 倒序遍历
    while (i--) {
        auto& entry = cats->list[I];
        // 新增的方法列表
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }
        // 新增属性列表
        property_list_t *proplist = 
            entry.cat->propertiesForMeta(isMeta, entry.hi);
        if (proplist) {
            proplists[propcount++] = proplist;
        }
        // 新增协议列表
        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}

void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;

        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            // 将原来的array()->lists后移,大小为addedCount
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            // 将新增的addedLists,添加到array()->lists中,从开头开始添加,大小为addedCount
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

从上面的代码看出,在类初始化的利用runtime将分类属性,方法,协议添加到原来类当中.

在之前的Rnu,Sleep分类中的分别实现Animal中的eat方法,我们调用eat方法,那到底会调那个eat方法.实际调用如下:

Snip20181110_1.png

调用了Run分类的eat方法.为什么会调用Run分类的eat方法?

此时编译的顺序为:

Snip20181110_4.png

改变Run分类和Sleep分类的编译顺序,

Snip20181110_5.png

此时调用为Sleep分类的eat方法:

Snip20181110_6.png

这是因为又前面的源码得知,当app按顺序编译后,运行的时候mlists是倒序添加的分类方法列表,调用attachLists()函数将mlists所有新增方法,放在array()->lists的开始.所以最后编译的分类方法,运行时添加在array()->lists的最前面.

所以Category的原理是:

_category_t并没有存放成员变量,所有我们在分类中并不能直接添加成员变量,但是我们可以间接用关联对象添加成员变量.比如我们给Run分类添加一个height属性(只生成setter,getter声明,不提供实现),方法如下:

void objc_setAssociatedObject(id object, const void * key, id value, objc_AssociationPolicy policy)

其中policy有四个值,

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    // 相当于assign
    OBJC_ASSOCIATION_ASSIGN = 0, 
    // 相当于strong, nonatomic         
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
   //  相当于copy, nonatomic
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3, 
    // 相当于strong, atomic  
    OBJC_ASSOCIATION_RETAIN = 01401,
    // 相当于copy, atomic
    OBJC_ASSOCIATION_COPY = 01403         
   };

setter实现如下:

- (void)setHeight:(double)height {
    objc_setAssociatedObject(self, @"height", @(height), OBJC_ASSOCIATION_ASSIGN);
}

getter实现如下:

- (double)height {
    return [objc_getAssociatedObject(self, @"height") doubleValue];
}

由前面的得知关联对象我们并没有实际生成成员变量,那到底底层实际是如何实现的呢?查看runtime源码,void objc_setAssociatedObject()代码如下:

void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
    _object_set_associative_reference(object, (void *)key, value, policy);
}

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {   // 关联对象管理类
        AssociationsManager manager;
        // AssociationsHashMap中参数中的DISGUISE(object)为key,值为ObjectAssociationMap
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        // value不为nil
        if (new_value) {
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            
            if (i != associations.end()) {
                // ObjectAssociationMap已经存在时
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    // value已经存在ObjectAssociationMap时
                    old_association = j->second;
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    // value没有存在ObjectAssociationMap时
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // ObjectAssociationMap不存在时
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
            
        } else {
            // 值为nil的时候,相当于移除关联关系
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    if (old_association.hasValue()) ReleaseValue()(old_association);
}

从上面源码得知:整个关联对象由AssociationsManager类管理,里面有一个map为AssociationsHashMap,key为由object参数生成的DISGUISE(object),值为一个map为ObjectAssociationMap,key为参数的key,值为参数的value.

Snip20181111_16.png

当移除某个key的关联关系时,直接将参数value设置为nil.

获取关联对象的值的源码为:

id objc_getAssociatedObject(id object, const void *key) {
    return _object_get_associative_reference(object, (void *)key);
}

id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            ObjectAssociationMap *refs = i->second;
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                ObjcAssociation &entry = j->second;
                value = entry.value();
                policy = entry.policy();
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        objc_autorelease(value);
    }
    return value;
}

知道了objc_setAssociatedObject()原理之后,也很容易理解objc_getAssociatedObject()的代码.

如果要移除object的所有关联关系,函数为void objc_removeAssociatedObjects(id object),其实就是移除AssociationsManagerAssociationsHashMap对应由DISGUISE(object)作为key的值,就是ObjectAssociationMap.

void objc_removeAssociatedObjects(id object) 
{
    if (object && object->hasAssociatedObjects()) {
        _object_remove_assocations(object);
    }
}

void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // 复制所有需要移除的关联对象
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            delete refs;
            associations.erase(i);
        }
    }
    for_each(elements.begin(), elements.end(), ReleaseValue());
}
上一篇下一篇

猜你喜欢

热点阅读