美文网首页
KVC调用规则

KVC调用规则

作者: MrOreo | 来源:发表于2018-07-05 17:13 被阅读0次
    每一天
    原创文章,转载请注明:转自:Try_Try_Try

    背景

    关于KVC的使用方法,网络上已有很多文章介绍,这篇文章讲解的也很全面(https://www.jianshu.com/p/45cbd324ea65)。最近被问到了很多的问题,因此会将问到的问题都总结一下,方便之后查看。

    很简单的一句话,描述一下kvc的get方法调用顺序?

    嗯哼,我伙呆,当时就火大。

    吐槽一下:感觉记住这个顺序用处不太大,可能当时记住了,之后就又忘了。我其实是比较反感问一些需要死记硬背,但是平时用处不太大的问题。如果当时有电脑,我直接可以进行演示操作,验证推倒。我直接说了句:忘了(对面坐的神 笑了一下,我内心开始不淡定,摸了摸后脑勺和鼻子,低下了头。)


    目录结构:

      1. 代码结构简介
      • 1.1 类结构
      • 1.2 代码展示
      1. KVC中get调用顺序
      • 2.1 观察图片总结结论
      • 2.2 验证
      1. KVC中set调用顺序

    正文:

    因为我们对于图片都是敏感的,所以文章中尽量给出图片方便记忆(就像我开头放的那张小魔仙的图片一样,现在你一定还记得---内心笑一下吧)。

    1. 代码结构简介

    1.1 类结构
    类结构
    1.2 代码展示

    其中Animate.h 和 Plant.h 的 实例变量是一样的。
    Animate.h

    #import <Foundation/Foundation.h>
    
    // 记录get
    @interface Animate : NSObject{
        
    //    @public
    //    NSString *_age;
    //    NSString *_isAge;
    //    NSString *age;
    //    NSString *isAge;
    }
    
    @end
    
    

    Animate.m

    #import "Animate.h"
    
    @implementation Animate
    
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    - (NSString *)getAge
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"1";
    }
    
    - (NSString *)age
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"2";
    }
    
    - (NSString *)isAge
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"3";
    }
    
    - (NSString *)_getAge
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"4";
    }
    
    - (NSString *)_age
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"5";
    }
    
    @end
    

    Plant.h

    #import <Foundation/Foundation.h>
    
    // 记录set
    @interface Plant : NSObject{
        
    //        @public
    //        NSString *_age;
    //        NSString *_isAge;
    //        NSString *age;
    //        NSString *isAge;
    }
    
    @end
    

    Plant.m

    #import "Plant.h"
    
    @implementation Plant
    
    - (void)setAge:(NSString *)age
    {
        NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
    }
    
    - (void)_setAge:(NSString *)age
    {
        NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
    }
    
    @end
    

    2. KVC中get调用顺序

    好吧,先来一张图,说明执行的顺序,接着验证

    2.1 观察图片总结结论
    图片
    图2.1 getValueForkey:

    从上图2.1中可以看出基本的调用顺序,关于为什么是这个流程,其实通过断点验证出来的,并没有从源代码中发现其具体实现过程(关于这点,我一直在查找资料,毕竟要见到本质才相信)。

    • 右边的方法,左边的字段;(先调用方法,再执行字段)
    • 方法调用顺序:getKey、key、isKey、_getKey、_key
    • 字段调用顺序:_key、_isKey、key、isKey
    2.2 验证

    查看文件Animate相关内容
    main.m中添加如下内容

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            
            // get
            Animate *animate = [[Animate alloc] init];
            animate->_age = @"1";
            animate->_isAge = @"2";
            animate->age = @"3";
            animate->isAge = @"4";
    
            NSString *age = [animate valueForKey:@"age"];
    
            NSLog(@"Hello World!");
        }
        return 0;
    }
    
    • 如果将Animate.h和Animate.m中的注释都放开。在NSLog(@"Hello World!");该行打印断点,可以验证出来先执行方法,再执行属性;
    • 验证方法的顺序:先把.h中字段注释,main.m中的字段赋值也进行注释,只留下Animate.m中的几个方法如下:
    - (NSString *)getAge
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"1";
    }
    
    - (NSString *)age
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"2";
    }
    
    - (NSString *)isAge
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"3";
    }
    
    - (NSString *)_getAge
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"4";
    }
    
    - (NSString *)_age
    {
        NSLog(@"%@", NSStringFromSelector(_cmd));
        return @"5";
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    

    运行后,发现只打印getAge, 接着注释getAge方法,发现打印的其他方法。依次重复几次,就能得出一个顺序:getKey、key、isKey、_getKey、_key

    • 验证公有字段的执行顺序:通过断点查看给哪个字段进行了赋值。然后注释该字段,依次操作,就可得到执行的顺序:_key、_isKey、key、isKey
    • accessInstanceVariablesDirectly方法默认是返回YES,从方法名称也可以看出来该方法的含义,它用来控制实例变量是否可以访问。

    3. KVC中set调用顺序

    同样再来一张图,说明执行的顺序,接着验证


    图3.1 setValue:forKey:

    从图3.1中可以发现和图2.1中左边的实例变量的调用顺序是一样的。只是方法调用少了几个。

    结论
    • 右边的方法,左边的字段;(先执行方法,再执行字段)
    • 方法调用顺序:setKey、_setKey
    • 字段调用顺序:_key、_isKey、key、isKey
    验证

    查看文件Plant相关内容
    main.m中有如下内容:

    int main(int argc, const char * argv[]) {
        @autoreleasepool {
           
            // set
            Plant *plant = [[Plant alloc] init];
            [plant setValue:@"2" forKey:@"age"];
            
            NSLog(@"Hello World!");
        }
        return 0;
    }
    
    • 验证的方法和上述get的操作类似,就会得出上图的结果。这里请自行验证。
      其中Plant.m中的方法如下:
    - (void)setAge:(NSString *)age
    {
        NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
    }
    
    - (void)_setAge:(NSString *)age
    {
        NSLog(@"%@--%@", NSStringFromSelector(_cmd), age);
    }
    

    通过验证,就会和图3.1总结的一样。
    关于更高深的其他操作这里不再做分析,只对其基本原理进行剖析。


    • 本文相关的demo已放置github.
    • 在阅读中,如果发现文章有不合理的地方,欢迎提出疑问至邮箱:B12050217@163.com.
    • 原创文章,转载请注明:转自:Try_Try_Try
    • OK, game over.

    相关文章

      网友评论

          本文标题:KVC调用规则

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