KVC (Key-Value coding)

Key code

The basic use

  1. Can assign values to private members of an object
  2. The packing and unpacking of numeric and structural properties

Example: WTPerson. H

#import <Foundation/Foundation. H > @interface WTPerson: NSObject{// @public //@protect default NSString * _name; } /** name **/ //@property(nonatomic,strong)NSString * name; @endCopy the code

ViewController.m

#import "ViewController.h" #import "WTPerson.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UITextField *text; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; WTPerson * p = [WTPerson new]; // access the member variable //p.name = @"wt"; //NSLog(@"%@",p.name); P ->_name = @"wt"; p->_name = @"wt"; //NSLog(@"%@",p->_name); [p setValue:@"wt" forKey:@"name"]; NSLog(@"%@",[p valueForKey:@"name"]); [self.text setValue:[UIColor redColor] forKeyPath:@"_placeholderLabel.textColor"]; }Copy the code

As a developer, it is especially important to have a learning atmosphere and a communication circle. This is my iOS communication group: 642363427, no matter you are white or big, welcome to join us. Share BAT, Ali interview questions, interview experience, discuss technology, and we can exchange, learn and grow together!

KVC assignment process analysis and customization and exception handling

Assignment process

  • 1. Find relevant methods firstset<Key>; _set<Key>; setIs<Key>;
  • 2. If there is no relevant method+(BOOL)accessInstanceVariablesDirectlyDetermines whether member variables can be accessed directly
  • 3. If NO is judged, directly execute KVCSetValue: forUndefinedKey: (system throws an exception, undefined key)
  • 4, if YES, continue to find related variables_<key> _is<Key> <key> is<Key>
  • 5. No method or member exists,setValue:forUndefinedKey:The default method is to throw an exception

To verify

WTPerson.h

#import <Foundation/Foundation. H > @interface WTPerson: NSObject{@public //@protect default NSString * _name; NSString * _isName; NSString * name; NSString * isName; } @endCopy the code

WTPerson.m

#import "WTPerson.h"
@implementation WTPerson

-(void)setName:(NSString *)name{
    NSLog(@"%s",__func__);
}

-(void)_setName:(NSString *)name{
    NSLog(@"%s",__func__);
}

-(void)setIsName:(NSString *)name{
    NSLog(@"%s",__func__);
}
@end
Copy the code

ViewController.m

#import "ViewController.h" #import "WTPerson.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; WTPerson * p = [WTPerson new]; [p setValue:@"wt" forKey:@"name"]; NSLog(@"name = %@",p->name); NSLog(@"_name = %@",p->_name); NSLog(@"isname = %@",p->isName); NSLog(@"_isname = %@",p->_isName); } @endCopy the code
  • Run the program, We take -(void)setName:(NSString *)name, -(void)_setName:(NSString *)name, -(void)setIsName:(NSString) *)name three methods are commented in sequence, and we find that all three methods are executed in sequence.

  • And then we take the NSString * _name in wtPerson. h; , nsstrings * _isName; , nsstrings * name; , nsstrings * isName; Commenting in turn, we find that the four attributes are assigned in turn.

In WTPerson. We let accessInstanceVariablesDirectly return NO m, the program directly to collapse.

+ (BOOL)accessInstanceVariablesDirectly{
    return NO;
}
Copy the code

Value process

  • 1. Find relevant methods firstget<Key>,key
  • 2. Without relevant methods,+(BOOL)accessInstanceVariabkesDirectlyDetermines whether member variables can be accessed directly
  • 3. If NO, execute KVC directlyvalueForUndefinedKey:(System throws an exception with undefined key)
  • 4, if YES, continue to find related variables_<key>, _is< key>, <key>, is< key>
  • 5. No method or member exists,valueForUndefineKey:Method, the default is to throw an exception

To verify

WTPerson.m

#import "WTPerson.h"

@implementation WTPerson

//- (NSString*) getName{
//    NSLog(@"%s",__func__);
//    return @"getName";
//}

- (NSString*) name {
    return @"name";
}

//+ (BOOL)accessInstanceVariablesDirectly{
//    return NO;
//}
@end
Copy the code

ViewController.m

#import "ViewController.h" #import "WTPerson.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UITextField *text; @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; WTPerson * p = [WTPerson new]; / / verification value KVC process NSLog (@ "name = % @," [p valueForKey: @ "name"]); } @endCopy the code

The value is roughly the same as the assignment.

KVC custom

Custom KVC code implementation

Create class NSObject+KVC

NSObject+KVC.h

#import <Foundation/Foundation.h>

@interface NSObject (KVC)

- (void)wt_setValue:(nullable id)value forKey:(NSString *)key;

- (id)wt_valueForKey:(NSString *)key;

@end
Copy the code

NSObject+KVC.m

#import "NSObject+KVC.h" #import <objc/runtime.h> @implementation NSObject (KVC) - (id)wt_valueForKey:(NSString *)key{ If (key == nil && key.length ==0) {return nil; } //Key NSString * Key = key.capitalizedString; Get <Key>, Key NSString * getKey = [NSString stringWithFormat:@"get%@:",Key]; if ([self respondsToSelector:NSSelectorFromString(getKey)]) { return [self performSelector:NSSelectorFromString(getKey)]; } if ([self respondsToSelector:NSSelectorFromString(key)]) { return [self performSelector:NSSelectorFromString(key)]; } if (! [self.class accessInstanceVariablesDirectly]) { NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"setValue:forUndefineKey" userInfo:nil]; @throw exception; } unsigned int count = 0; Ivar * ivars = class_copyIvarList([self class], &count); NSMutableArray * arr = [[NSMutableArray alloc]init]; for (int i = 0; i<count; i++) { Ivar var = ivars[i]; const char * varName = ivar_getName(var); NSString *name = [NSString stringWithUTF8String:varName]; [arr addObject:name]; } //_<key> _is<Key> <key> is<Key> for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) { return object_getIvar(self, ivars[i]); } } for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) { return object_getIvar(self, ivars[i]); } } for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) { return object_getIvar(self, ivars[i]); } } for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) { return object_getIvar(self, ivars[i]); } } free(ivars); return nil; } - (void)wt_setValue:(nullable id)value forKey:(NSString *)key{// if (key == nil && key.length ==0) {return; } //Key NSString * Key = key.capitalizedString; Set <Key>; _set<Key>; setIs<Key>; NSString * setKey = [NSString stringWithFormat:@"set%@:",Key]; if ([self respondsToSelector:NSSelectorFromString(setKey)]) { [self performSelector:NSSelectorFromString(setKey) withObject:value]; return; } NSString * _setKey = [NSString stringWithFormat:@"_set%@:",Key]; if ([self respondsToSelector:NSSelectorFromString(_setKey)]) { [self performSelector:NSSelectorFromString(_setKey) withObject:value]; return; } NSString * setIsKey = [NSString stringWithFormat:@"setIs%@:",Key]; if ([self respondsToSelector:NSSelectorFromString(setIsKey)]) { [self performSelector:NSSelectorFromString(setIsKey) withObject:value]; return; } if (! [self.class accessInstanceVariablesDirectly]) { NSException * exception = [NSException exceptionWithName:@"NSUnknownKeyException" reason:@"setValue:forUndefineKey" userInfo:nil]; @throw exception; } unsigned int count = 0; Ivar * ivars = class_copyIvarList([self class], &count); NSMutableArray * arr = [[NSMutableArray alloc]init]; for (int i = 0; i<count; i++) { Ivar var = ivars[i]; const char * varName = ivar_getName(var); NSString *name = [NSString stringWithUTF8String:varName]; [arr addObject:name]; } //_<key> _is<Key> <key> is<Key> for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) { object_setIvar(self, ivars[i], value); free(ivars); return; } } for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",Key]]) { object_setIvar(self, ivars[i], value); free(ivars); return; } } for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"%@",key]]) { object_setIvar(self, ivars[i], value); free(ivars); return; } } for (int i = 0; i < count; i++) { NSString *keyName = arr[i]; if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",Key]]) { object_setIvar(self, ivars[i], value); free(ivars); return; } } [self setValue:value forUndefinedKey:Key]; free(ivars); } @endCopy the code

validation

ViewController.m

#import "ViewController.h"
#import "WTPerson.h"
#import "NSObject+KVC.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    WTPerson * p =[WTPerson new];
    [p wt_setValue:@"wt" forKey:@"name"];

    NSLog(@"name-KVC = %@",[p wt_valueForKey:@"name"]);
    NSLog(@"_name = %@",p->_name);
    NSLog(@"_isName = %@",p->_isName);
    NSLog(@"name = %@",p->name);
    NSLog(@"isName = %@",p->isName);
}
@end
Copy the code

In the project commond+shift+o search setValue:forKey found in the Foundation framework under the NSKeyValueCoding file

When we look at the methods in this file, we find that this file is a collection of categories

KVC exception handling and correctness verification

KVC exception handling

  • 1. Null assignmentsetNilValueForKey
  • 2. The Key value does not existsetValue:forUndefinedKey

Correctness verification

ValidateValue This method works as follows:

  • 1. Find out if your class implements a method-(BOOL)validate<Key>:error;
  • 2. If implemented, return NO or YES based on the custom logic in the implementation method. If this method is not implemented, the system returns YES by default

The sample code

WTPerson… h

#import <Foundation/Foundation.h>

@interface WTPerson : NSObject

/** name  **/
@property(nonatomic,strong)NSString * name;

/** age  **/
@property(nonatomic,assign)int age;

@end
Copy the code

WTPerson.m

#import "wtPerson. h" @implementation WTPerson - (void) setNilValueForKey:(NSString *)key{NSLog(@"%@ value cannot be empty ",key); } - (void) setValue (id)value forUndefinedKey (NSString *)key{NSLog(@"key = %@ ",key); ValueForUndefinedKey :(NSString *)key{NSLog(@"key = %@ value does not exist ",key); return nil; } - (BOOL) validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{ NSNumber* value = (NSNumber*)*ioValue; NSLog(@"%@",value); if ([value integerValue] >= 0 && [value integerValue] <= 200) { return YES; } return NO; } @endCopy the code

ViewController.m

#import "ViewController.h" #import "WTPerson.h" #import "WTContainer.h" @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; WTPerson * p = [WTPerson new]; [p setValue:@18 forKey:@"name"]; [p setValue:nil forKey:@"name"]; NSLog(@"name = %@",p.name); [p setValue:nil forKey:@"age"]; NSLog(@"age = %d",p.age); [p setValue:@"hello" forKey:@"name1"]; NSLog(@"name = %@",[p valueForKey:@"name1"]); WTContainer * container = [WTContainer new]; [container setValue:@"wt" forKey:@"name"]; [container setValue:@18 forKey:@"age"]; NSLog(@"name = %@,age = %@",[container valueForKey:@"name"],[container valueForKey:@"age"]); NSNumber * value = @200; NSNumber * value1 = @199; if ([p validateValue:&value1 forKey:@"age" error:NULL]) { [p setValue:value1 forKey:@"age"]; } NSLog(@"%@",[p valueForKey:@"age"]); } @endCopy the code