一.KVC的基本概念
Key-value coding,它是一种使用字符串标识符,间接访问对象属性的机制,而不是直接调用getter 和 setter方法。通常我们使用valueForKey 来替代getter 方法,setValue:forKey来代替setter方法。
下面,给大家一个例子,基础流程是这样的:
首先,创建基类,主要方法:
//根据这个来把key-value 自动分配到对应的属性 - (id) initWithDictionary:(NSMutableDictionary *) jsonObject;
然后,创建model类,继承自基类
model类需要实现方法
//如果遇到字典的key与model类中的属性名称不对应的情况,需要自己处理 - (void)setValue:(id)value forUndefinedKey:(NSString *)key //一般的赋值,key与model类中的属性名相同,处理一些特殊逻辑 - (void)setValue:(id)value forKey:(NSString *)key
各位还是通过代码来看下吧:
基类:JSONModel.h
// // JSONModel.h // iHotelApp_Demo // // Created by Eric on 14-1-13. // Copyright (c) 2014年 Eric. All rights reserved. // #import <Foundation/Foundation.h> @interface JSONModel : NSObject<NSCopying,NSCoding,NSMutableCopying> { } - (id) initWithDictionary:(NSMutableDictionary *) jsonObject; @end
// // JSONModel.m // iHotelApp_Demo // // Created by Eric on 14-1-13. // Copyright (c) 2014年 Eric. All rights reserved. // #import "JSONModel.h" @implementation JSONModel //根据字典初始化 - (id)initWithDictionary:(NSMutableDictionary *)jsonObject { self = [super init]; if (self) { [self setValuesForKeysWithDictionary:jsonObject]; } return self; } //允许编码 - (BOOL) allowsKeyedCoding { return YES; } - (id) initWithCoder:(NSCoder *)aDecoder { return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { //do nothing } //复制一个副本 - (id)mutableCopyWithZone:(NSZone *)zone { JSONModel *newModel = [[JSONModel allocWithZone:zone] init]; return newModel; } //复制一个副本 - (id)copyWithZone:(NSZone *)zone { JSONModel *newModel = [[[self class] allocWithZone:zone] init]; return newModel; } //找未找到的Key - (id) valueForUndefinedKey:(NSString *)key { NSLog(@"Undefined Key: %@",key); return nil; } //设置未找到的Key - (void)setValue:(id)value forUndefinedKey:(NSString *)key { NSLog(@"Undefined Key: %@",key); } @end
// // MenuItem.h // iHotelApp_Demo // // Created by Eric on 14-1-14. // Copyright (c) 2014年 Eric. All rights reserved. // #import "JSONModel.h" #import <Foundation/Foundation.h> /* "id": "JAP122", "image": "http://d1.myhotel.com/food_image1.jpg", "name": "Teriyaki Bento", "spicyLevel": 2, "rating" : 4, "description" : "Teriyaki Bento is one of the best lorem ipsum dolor sit", "waitingTime" : "930", "reviewCount" : 4 */ @interface MenuItem : JSONModel @property (nonatomic,strong) NSString *itemId; @property (nonatomic,strong) NSString *image; @property (nonatomic,strong) NSString *name; @property (nonatomic,strong) NSString *spicyLevel; @property (nonatomic,strong) NSString *rating; @property (nonatomic,strong) NSString *itemDescription; @property (nonatomic,strong) NSString *waitingTime; @property (nonatomic,strong) NSString *reviewCount; @property (nonatomic,strong) NSMutableArray *reviews; @end
MenuItem 自定义实现解码,编码的方法,这样,基类JSONModel中的
initWithDictionary方法,便可以实现key-value与类属性的相对应
// // MenuItem.m // iHotelApp_Demo // // Created by Eric on 14-1-14. // Copyright (c) 2014年 Eric. All rights reserved. // #import "MenuItem.h" #import "Review.h" @implementation MenuItem - (id)init { self = [super init]; if (self) { self.reviews = [NSMutableArray array]; } return self; } //在赋值的过程中,如果发现未声明的Key - (void)setValue:(id)value forUndefinedKey:(NSString *)key { if ([key isEqualToString:@"id"]) { self.itemId = value; } else if([key isEqualToString:@"description"]){ self.itemDescription = value; }else{ [super setValue:value forUndefinedKey:key]; } } //赋值 - (void)setValue:(id)value forKey:(NSString *)key { if ([key isEqualToString:@"reviews"]) { for (NSMutableDictionary *reviewArrayDic in value) { Review *thisReview = [[Review alloc] initWithDictionary:reviewArrayDic]; [self.reviews addObject:thisReview]; } }else{ [super setValue:value forKey:key]; } } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.itemId forKey:@"itemId"]; [aCoder encodeObject:self.image forKey:@"image"]; [aCoder encodeObject:self.name forKey:@"name"]; [aCoder encodeObject:self.spicyLevel forKey:@"spicyLevel"]; [aCoder encodeObject:self.rating forKey:@"rating"]; [aCoder encodeObject:self.itemDescription forKey:@"itemDescription"]; [aCoder encodeObject:self.waitingTime forKey:@"waitingTime"]; [aCoder encodeObject:self.reviewCount forKey:@"reviewCount"]; [aCoder encodeObject:self.reviews forKey:@"reviews"]; } - (id)initWithCoder:(NSCoder *)decoder { self = [super init]; if (self) { self.itemId = [decoder decodeObjectForKey:@"itemId"]; self.image = [decoder decodeObjectForKey:@"image"]; self.name = [decoder decodeObjectForKey:@"name"]; self.spicyLevel = [decoder decodeObjectForKey:@"spicyLevel"]; self.rating = [decoder decodeObjectForKey:@"rating"]; self.itemDescription = [decoder decodeObjectForKey:@"itemDescription"]; self.waitingTime = [decoder decodeObjectForKey:@"waitingTime"]; self.reviewCount = [decoder decodeObjectForKey:@"reviewCount"]; self.reviews = [decoder decodeObjectForKey:@"reviews"]; } return self; } - (id)copyWithZone:(NSZone *)zone { id theCopy = [[[self class] allocWithZone:zone] init]; // use designated initializer [theCopy setItemId:[self.itemId copy]]; [theCopy setImage:[self.image copy]]; [theCopy setName:[self.name copy]]; [theCopy setSpicyLevel:[self.spicyLevel copy]]; [theCopy setRating:[self.rating copy]]; [theCopy setItemDescription:[self.itemDescription copy]]; [theCopy setWaitingTime:[self.waitingTime copy]]; [theCopy setReviewCount:[self.reviewCount copy]]; [theCopy setReviews:[self.reviews copy]]; return theCopy; } @end
RESTfulEngine.m
//根据dict得到MenuItem实例 [[MenuItem alloc] initWithDictionary:obj]
- (RESTfulOperation *)fetchMenuItemsOnSucceeded:(ArrayBlock)succeedBlock onError:(ErrorBlock)errorBlock { RESTfulOperation *op = (RESTfulOperation *)[self operationWithPath:MENU_ITEMS_URL]; [op onCompletion:^(MKNetworkOperation *completedOperation) { //得到返回的JSON字符串 NSMutableDictionary *responseDictionary = [completedOperation responseJSON]; //得到字符串中的menuitems的子节点的内容 NSMutableArray *menuItemsJson = [responseDictionary objectForKey:@"menuitems"]; NSMutableArray *menuItems = [NSMutableArray array]; //遍历节点, [menuItemsJson enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { //然后将其中的key-value 通过 initWithDictionary 给类的属性赋值 [menuItems addObject:[[MenuItem alloc] initWithDictionary:obj]]; }]; succeedBlock(menuItems); } onError:errorBlock]; [self enqueueOperation:op]; return op; }