美文网首页
KVC的底层原理

KVC的底层原理

作者: 我是晶锅 | 来源:发表于2019-04-24 23:02 被阅读0次
    成员变量&属性变量&实例变量的区别
    // 成员变量
    @interface LGPerson : NSObject{
        @public
        NSString *myName; //成员
        id hello; // id - > class
        int age;
    }
    // 属性
    @property (nonatomic, copy) NSString *namep;
    @property (nonatomic, strong) LGSon  *son;
    

    说明:

    1. 属性在LLVM编译的作用下会自动生成setter和getter方法;
    2. 当没有可以匹配的成员变量时,会自动生成一个带下划线的成员变量;
    3. 实例变量是一种特殊的成员变量,也就是其中通过类对象实例出来的变量。

    KVC官方文档地址

    KVC的取值和赋值流程

    valueForKey取值流程:

    1. 先从方法中找寻,方法顺序:get<Key>, <key>, is<Key>, _<key>
    - (NSString *)getName {
        return @"getName";
    }
    - (NSString *)name {
        return @"name";
    }
    - (NSString *)isName {
        return @"isName";
    }
    - (NSString *)_name {
        return @"_name";
    }
    
    1. 如果1中按照方法没有找到,会按照数组处理,查看这几个方法:countOf<Key> and objectIn<Key>AtIndex:等
    2. 如果2中数组还没有找到,会按照集合方法处理:countOf<Key>, enumeratorOf<Key>, and memberOf<Key>
    3. 如果上面还没有找到,会通过成员变量直接取值获取,成员变量获取顺序:_<key>, _is<Key>, <key>, or is<Key>
    4. 如果返回的数量级支持NSNumber,就以NSNumber返回,如果不支持,就以NSValue返回
    5. 如果都失败了,调用valueForUndefinedKey:
    Category的加载

    分类会覆盖原类中的方法吗?
    我们在类和该类的分类中写了同样的方法,然后调用,发现执行的是分类中方法。但是其实原类中的方法并没有被覆盖,我们同时打印出该类中所有的方法可以看出,其中其实是有两个相同的方法的。


    根本原因

    根本原因是方法的加载方式。其实method,property,protocol都是加载类的相应的表中的。

    1. 方法自然加入到methods表中,通过下图中的attachLists方法。
    2. 当有新的方法,或者category中的方法,都通过这个方法加入到表中。
    3. 加入到表中时,会有一定的算法,因为算法复杂度的问题,新加入的方法为了不改变之前方法的顺序,直接插入到后面。
    4. 所以在后面的方法会先执行,也就是category会覆盖原类的假象。


      image.png
    load方法调用

    在prepare_load_methods函数中:
    先将类中的方法加入到loadable_classes队列中;
    其中会将superClass中的load方法也加入到loadable_classes队列中;
    最后将category中的load方法加入到loadable_classes队列中。
    准备好了,就执行了。

    void prepare_load_methods(const headerType *mhdr)
    {
        size_t count, i;
    
        runtimeLock.assertLocked();
    
        classref_t *classlist = 
            _getObjc2NonlazyClassList(mhdr, &count);
        for (i = 0; i < count; i++) {
            schedule_class_load(remapClass(classlist[i]));
        }
    
        category_t **categorylist = _getObjc2NonlazyCategoryList(mhdr, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = categorylist[i];
            Class cls = remapClass(cat->cls);
            if (!cls) continue;  // category for ignored weak-linked class
            realizeClass(cls);
            assert(cls->ISA()->isRealized());
            add_category_to_loadable_list(cat);
        }
    }
    
    static void schedule_class_load(Class cls)
    {
        if (!cls) return;
        assert(cls->isRealized());  // _read_images should realize
    
        if (cls->data()->flags & RW_LOADED) return;
    
        // Ensure superclass-first ordering
        schedule_class_load(cls->superclass);
    
        add_class_to_loadable_list(cls);
        cls->setInfo(RW_LOADED); 
    }
    

    准备好了就执行call_class_loads,调用load方法

    static void call_class_loads(void)
    {
        int i;
        
        // Detach current loadable list.
        struct loadable_class *classes = loadable_classes;
        int used = loadable_classes_used;
        loadable_classes = nil;
        loadable_classes_allocated = 0;
        loadable_classes_used = 0;
        
        // Call all +loads for the detached list.
        for (i = 0; i < used; i++) {
            Class cls = classes[i].cls;
            load_method_t load_method = (load_method_t)classes[i].method;
            if (!cls) continue; 
    
            if (PrintLoading) {
                _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
            }
            (*load_method)(cls, SEL_load);
        }
        
        // Destroy the detached list.
        if (classes) free(classes);
    }
    

    相关文章

      网友评论

          本文标题:KVC的底层原理

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