美文网首页
KVC 的本质分析

KVC 的本质分析

作者: SKY_Tang | 来源:发表于2018-09-18 18:28 被阅读0次

    KVC 全称 Key-Value Coding,俗称“键值编码”,可以通过一个key 来访问某个属性

    常见的 API

    • - (void)setValue:(id)value forKeyPath:(NSString *)keyPath;
    • - (void)setValue:(id)value forKey:(NSString *)key;
    • - (id)valueForKeyPath:(NSString *)keyPath;
    • - (id)valueForKey:(NSString *)key;

    看赋值setValue:forKey:的原理

    setValue:forKey:

    来验证过程

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
        int _age;
        int _isAge;
        int age;
        int isAge;
    }
    
    @end
    
    #import "MJPerson.h"
    
    @implementation MJPerson
    
    - (void)setAge:(int)age
    {
        NSLog(@"setAge");
    }
    
    - (void)_setAge:(int)age
    {
        NSLog(@"_setAge");
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    #import <Foundation/Foundation.h>
    #import "MJPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            MJPerson *person = [[MJPerson alloc] init];
            
            [person setValue:@10 forKey:@"age"];
        }
        return 0;
    }
    

    断点打印

    打印

    打印看出,成员变量都没赋值,只走了setAge:方法

    注释掉setAge:方法,再断点打印

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (void)setAge:(int)age
    //{
    //    NSLog(@"setAge");
    //}
    
    - (void)_setAge:(int)age
    {
        NSLog(@"_setAge");
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    注释掉`setAge:`方法

    打印看出,成员变量都没赋值,只走了_setAge:方法

    注释掉_setAge:方法,accessInstanceVariablesDirectly返回NO,再断点打印

    @implementation MJPerson
    
    //- (void)setAge:(int)age
    //{
    //    NSLog(@"setAge");
    //}
    
    //- (void)_setAge:(int)age
    //{
    //    NSLog(@"_setAge");
    //}
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return NO;
    }
    
    @end
    
    注释掉`_setAge:`方法,`accessInstanceVariablesDirectly`返回`NO`

    打印看出,直接报错NSUnknownKeyException

    设置accessInstanceVariablesDirectly返回YES,再断点打印

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (void)setAge:(int)age
    //{
    //    NSLog(@"setAge");
    //}
    
    //- (void)_setAge:(int)age
    //{
    //    NSLog(@"_setAge");
    //}
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    设置`accessInstanceVariablesDirectly`返回`YES`

    打印看出只有_age被赋值

    注释掉_age,断点打印

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
        int _isAge;
        int age;
        int isAge;
    }
    
    @end
    
    注释掉`_age`

    打印看出只有_isAge被赋值

    注释掉_isAge,断点打印

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
    //    int _isAge;
        int age;
        int isAge;
    }
    
    @end
    
    注释掉`_isAge`

    打印看出只有age被赋值

    注释掉age,断点打印

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
    //    int _isAge;
    //    int age;
        int isAge;
    }
    
    @end
    
    注释掉`age`

    打印看出只有isAge被赋值

    注释掉isAge,断点打印

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
    //    int _isAge;
    //    int age;
    //    int isAge;
    }
    
    @end
    
    注释掉`isAge`

    打印看出,直接报错NSUnknownKeyException

    看取值valueForKey:的原理

    valueForKey:

    来验证过程

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
        int _age;
        int _isAge;
        int age;
        int isAge;
    }
    
    @end
    
    #import "MJPerson.h"
    
    @implementation MJPerson
    
    - (int)getAge
    {
        return 1;
    }
    
    - (int)age
    {
        return 2;
    }
    
    - (int)isAge
    {
        return 3;
    }
    
    - (int)_age
    {
        return 4;
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    #import <Foundation/Foundation.h>
    #import "MJPerson.h"
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            MJPerson *person = [[MJPerson alloc] init];
            person->_age = 6;
            person->_isAge = 7;
            person->age = 8;
            person->isAge = 9;
            
            NSLog(@"age------%@", [person valueForKey:@"age"]);
        }
        return 0;
    }
    

    打印结果

    打印结果

    打印看出,只走了getAge方法

    注释掉getAge方法

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (int)getAge
    //{
    //    return 1;
    //}
    
    - (int)age
    {
        return 2;
    }
    
    - (int)isAge
    {
        return 3;
    }
    
    - (int)_age
    {
        return 4;
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    注释掉`getAge`方法

    打印看出,只走了age方法

    注释掉age方法

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (int)getAge
    //{
    //    return 1;
    //}
    
    //- (int)age
    //{
    //    return 2;
    //}
    
    - (int)isAge
    {
        return 3;
    }
    
    - (int)_age
    {
        return 4;
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    注释掉`age`方法

    打印看出,只走了isAge方法

    注释掉isAge方法

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (int)getAge
    //{
    //    return 1;
    //}
    
    //- (int)age
    //{
    //    return 2;
    //}
    
    //- (int)isAge
    //{
    //    return 3;
    //}
    
    - (int)_age
    {
        return 4;
    }
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    注释掉`isAge`方法

    打印看出,只走了_age方法

    注释掉_age方法,accessInstanceVariablesDirectly返回NO

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (int)getAge
    //{
    //    return 1;
    //}
    
    //- (int)age
    //{
    //    return 2;
    //}
    
    //- (int)isAge
    //{
    //    return 3;
    //}
    
    //- (int)_age
    //{
    //    return 4;
    //}
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return NO;
    }
    
    @end
    
    注释掉`_age`方法

    打印看出,直接报错NSUnknownKeyException

    设置accessInstanceVariablesDirectly返回YES

    #import "MJPerson.h"
    
    @implementation MJPerson
    
    //- (int)getAge
    //{
    //    return 1;
    //}
    
    //- (int)age
    //{
    //    return 2;
    //}
    
    //- (int)isAge
    //{
    //    return 3;
    //}
    
    //- (int)_age
    //{
    //    return 4;
    //}
    
    + (BOOL)accessInstanceVariablesDirectly
    {
        return YES;
    }
    
    @end
    
    设置`accessInstanceVariablesDirectly`返回`YES`

    打印的是成员变量_age的值

    注释掉成员变量_age

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
        int _isAge;
        int age;
        int isAge;
    }
    
    @end
    
    注释掉成员变量`_age`

    打印的是成员变量_isAge的值

    注释掉成员变量_isAge

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
    //    int _isAge;
        int age;
        int isAge;
    }
    
    @end
    
    注释掉成员变量`_isAge`

    打印的是成员变量age的值

    注释掉成员变量age

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
    //    int _isAge;
    //    int age;
        int isAge;
    }
    
    @end
    
    注释掉成员变量`age`

    打印的是成员变量isAge的值

    注释掉成员变量isAge

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        @public
    //    int _age;
    //    int _isAge;
    //    int age;
    //    int isAge;
    }
    
    @end
    
    注释掉成员变量`isAge`

    打印看出,直接报错NSUnknownKeyException

    看一下KVC会不会触发KVO

    #import <Foundation/Foundation.h>
    
    @interface MJPerson : NSObject {
        int _age;
    }
    
    @end
    
    #import "MJPerson.h"
    
    @implementation MJPerson
    
    @end
    
    #import "ViewController.h"
    #import "MJPerson.h"
    
    @interface ViewController ()
    
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        MJPerson *person = [[MJPerson alloc] init];
        
        [person addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:NULL];
        
        [person setValue:@10 forKey:@"age"];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context
    {
        NSLog(@"监听到%@的%@属性值改变了 - %@ - %@", object, keyPath, change, context);
    }
    
    @end
    
    打印结果

    打印结果看到,触发了KVO。但是MJPerson只有一个成员变量_age,也就是不会触发setAge:方法,但是依然触发了KVO

    通过《KVO的本质分析》,我们知道 在KVC的时候肯定是执行了willChangeValueForKey:didChangeValueForKey:方法。

    面试题

    通过KVC修改属性会触发KVO么?

    会触发KVO

    KVC的赋值和取值过程是怎样的?原理是什么?

    把上面两个图讲清楚

    相关文章

      网友评论

          本文标题:KVC 的本质分析

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