KVC and setValue: forUndefinedKey: method, kvcsetvalue
During actual development and application, the number of dictionary keys constructed from external data is usually higher than the number of attributes in the Custom Data Model.
For example, obtaining data in JSON format from the outside contains five keys, as shown below:
{ "cityname" : "beijing", "state1" : "0", "state2" : "1", "tem1" : "25", "tem2" : "14",}
The corresponding model only contains three attributes:
/** City name */@ property (copy, nonatomic) NSString * cityname;/** minimum temperature */@ property (copy, nonatomic) NSNumber * tem1; /** maximum temperature */@ property (copy, nonatomic) NSNumber * tem2;
The sample code is as follows:
Controller ViewController. m:
# Import "ViewController. h "# import" Weather. h "@ interface ViewController () @ end @ implementation ViewController-(void) viewDidLoad {[super viewDidLoad]; // read external data in JSON format NSURL * url = [[NSBundle mainBundle] URLForResource: @ "weather" withExtension: @ "json"]; NSData * data = [NSData dataWithContentsOfURL: url]; NSDictionary * dict = [NSJSONSerialization JSONObjectWithData: data options: NSJSONReadingMutableContainers error: NULL]; // data model instance Weather * w = [Weather weatherWithDictionary: dict]; NSLog (@ "% @", w) ;}@ end
Model class Weather. h:
# Import <Foundation/Foundation. h> @ interface Weather: NSObject/** city name */@ property (copy, nonatomic) NSString * cityname;/** minimum temperature */@ property (copy, nonatomic) NSNumber * tem1;/** maximum temperature */@ property (copy, nonatomic) NSNumber * tem2;-(instancetype) initWithDictionary :( NSDictionary *) dict; + (instancetype) weatherWithDictionary :( NSDictionary *) dict; @ end
Model class Weather. m:
#import "Weather.h"@implementation Weather- (instancetype)initWithDictionary:(NSDictionary *)dict { self = [super init]; if (nil != self) { [self setValuesForKeysWithDictionary:dict]; } return self;}+ (instancetype)weatherWithDictionary:(NSDictionary *)dict { return [[self alloc] initWithDictionary:dict];}- (NSString *)description { return [NSString stringWithFormat:@"[cityname, tem1, tem2] = [%@, %@, %@]", self.cityname, self.tem1, self.tem2];}@end
If you run the program, the following error message is reported:
*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[ setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key state1.'*** First throw call stack:(0 CoreFoundation 0x000000010e158c65 __exceptionPreprocess + 1651 libobjc.A.dylib 0x000000010ddefbb7 objc_exception_throw + 452 CoreFoundation 0x000000010e1588a9 -[NSException raise] + 93 Foundation 0x000000010d984b53 -[NSObject(NSKeyValueCoding) setValue:forKey:] + 2594 Foundation 0x000000010d9c6fad -[NSObject(NSKeyValueCoding) setValuesForKeysWithDictionary:] + 2615 2015-05-05-setValueforUndefinedKey 0x000000010d8b94bd -[Weather initWithDictionary:] + 1416 2015-05-05-setValueforUndefinedKey 0x000000010d8b9557 +[Weather weatherWithDictionary:] + 877 2015-05-05-setValueforUndefinedKey 0x000000010d8b9925 -[ViewController viewDidLoad] + 2778 UIKit 0x000000010e683210 -[UIViewController loadViewIfRequired] + 7389 UIKit 0x000000010e68340e -[UIViewController view] + 2710 UIKit 0x000000010e59e2c9 -[UIWindow addRootViewControllerViewIfPossible] + 5811 UIKit 0x000000010e59e68f -[UIWindow _setHidden:forced:] + 24712 UIKit 0x000000011bb4a175 -[UIWindowAccessibility _orderFrontWithoutMakingKey] + 6813 UIKit 0x000000010e5aae21 -[UIWindow makeKeyAndVisible] + 4214 UIKit 0x000000010e54e457 -[UIApplication _callInitializationDelegatesForMainScene:transitionContext:] + 273215 UIKit 0x000000010e5511de -[UIApplication _runWithMainScene:transitionContext:completion:] + 134916 UIKit 0x000000010e5500d5 -[UIApplication workspaceDidEndTransaction:] + 17917 FrontBoardServices 0x0000000110d575e5 __31-[FBSSerialQueue performAsync:]_block_invoke_2 + 2118 CoreFoundation 0x000000010e08c41c __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__ + 1219 CoreFoundation 0x000000010e082165 __CFRunLoopDoBlocks + 34120 CoreFoundation 0x000000010e081f25 __CFRunLoopRun + 238921 CoreFoundation 0x000000010e081366 CFRunLoopRunSpecific + 47022 UIKit 0x000000010e54fb42 -[UIApplication _run] + 41323 UIKit 0x000000010e552900 UIApplicationMain + 128224 2015-05-05-setValueforUndefinedKey 0x000000010d8b9c7f main + 11125 libdyld.dylib 0x0000000110727145 start + 126 ??? 0x0000000000000001 0x0 + 1)libc++abi.dylib: terminating with uncaught exception of type NSException
When setValuesForKeysWithDictionary: method is used, the system will automatically call setValue: forUndefinedKey: This method for attributes that are missing in the data model and cannot be paired with any keys, the default implementation of this method will cause an NSUndefinedKeyExceptiony exception.
If you want the program to run without causing any exception information and work properly, you can have the data model class override setValue: forUndefinedKey: Method to override the default implementation, in addition, two parameters of this method can be used to obtain the key value that cannot be paired.
The modified model Weather. m:
# Import "Weather. h" @ implementation Weather-(instancetype) initWithDictionary :( NSDictionary *) dict {self = [super init]; if (nil! = Self) {[self setValuesForKeysWithDictionary: dict];} return self;} // rewrite setValue: forUndefinedKey: method-(void) setValue :( id) value forUndefinedKey :( NSString *) key {NSLog (@ "key = % @, value = % @", key, value) ;}+ (instancetype) weatherWithDictionary :( NSDictionary *) dict {return [[self alloc] initWithDictionary: dict] ;}- (NSString *) description {return [NSString stringWithFormat: @ "[cityname, tem1, tem2] = [% @, % @, % @] ", self. cityname, self. tem1, self. tem2];} @ end
In this example, override setValue: forUndefinedKey: method, but do not perform any substantive operations on key values that are not paired to ignore them. Of course, you can also process the key value in the method body.
After the modification is completed, the program output is as follows:
key = state1, value = 0key = state2, value = 1[cityname, tem1, tem2] = [beijing, 25, 14]