KVC
KVC基本使用
- 能够对对象的私有成员进行取值赋值
- 对数值和结构体型的属性进行的打包解包处理
KVC赋值取值过程分析和自定义及异常处理
赋值过程:
- 先找相关方法
set<Key>; _set<Key>; setIs<Key>;
- 若没有相关方法
+(BOOL)accessInstanceVariablesDirectly
,判断是否可以直接方法成员变量 - 如果是判断是NO,直接执行
setValue:forUndefinedKey:(系统抛出一个异常,未定义key)
- 如果是YES,继续找相关变量
_<key> _is<Key> <key> is<Key>
- 方法或成员都不存在,
setValue:forUndefinedKey:
默认是抛出异常
取值过程:
- 先找相关方法
get<Key>,key
- 若没有相关方法
+(BOOL)accessInstanceVariabkesDirectly
,判断是否可以直接方法成员变量 - 如果是判断是NO,直接执行KVC的
valueForUndefinedKey:(系统抛出一个异常,未定义key)
- 如果是YES,继续找相关变量
_<key>、_is<Key>、<key>、is<Key>
- 方法或成员都不存在,
valueForUndefineKey:
方法,默认是抛出异常
自定义KVC
#import "NSObject+KVC.h"
#import <objc/runtime.h>
@implementation NSObject (KVC)
- (void)bear_setValue:(nullable id)value forKey:(NSString *)key {
// 判断是否合法
if (key == nil && key.length == 0) {
return;
}
// Key
NSString* Key = key.capitalizedString;
/// 先找相关方法
//set:, _set:, setIs:
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:@"NSUnkonwnKeyException" 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];
}
// _ _is is
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);
}
@end
异常处理:
- 值类型赋值为空(id类型不会),报错!可通过setNilValueForKey:捕获
- 赋值Key不存在,可通过setValue:forUndefinedKey捕获
- 取值Key不存在,可通过valueForUndefinedKey:捕获
正确性验证
NSNumber* value = @200;
if ([p validateValue:&value forKey:@"age" error:NULL]) {
[p setValue:value forKey:@"age"];
}
- validateValue:该方法的工作原理:
- 先找一下你的类中是否实现了方法
-(BOOL)validate:error:
- 如果实现了就会根据实现方法里面的自定义逻辑返回NO或者YES,如果没有实现这个方法,则系统默认返回就是YES
- 先找一下你的类中是否实现了方法
KVC进阶用法
KVC与字典
- (void) dictionaryTest {
TZPerson* p = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@18,
@"nick":@"Cat",
@"height":@180,
@"dd":@"helo"
};
[p setValuesForKeysWithDictionary:dict];
NSLog(@"p.name = %@, p.age = %d, p.nick =%@, p.height = %f", p.name, p.age, p.nick, p.height);
NSArray* keys = @[@"name", @"age"];
NSDictionary* dict1 = [p dictionaryWithValuesForKeys:keys];
NSLog(@"%@", dict1);
}
KVC的消息传递
/// KVC消息传递 array
- (void) arrayKVCTest {
NSArray* arr = @[@"Monday", @"Tuesday", @"Wednesday"];
//相当于给每个成员d发送length消息
NSArray* lengthArr = [arr valueForKey:@"length"];
NSLog(@"%@", lengthArr);
//发送变小写、消息
NSArray* lowercaseArr = [arr valueForKey:@"lowercaseString"];
NSLog(@"%@", lowercaseArr);
}
KVC容器操作
/// 聚合操作符 公式:valueForKeyPath:@+集合操作符+.路径
// @avg、@count、@max、@min、@sum
- (void) contrainerTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
//获取所有成员
NSLog(@"%@", [students valueForKey:@"height"]);
/// 平均身高
float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
NSLog(@"%f", avg);
}
/// 数组操作符 @distinctUnionOfObjects @unionOfObjects
- (void) contrainerArrayTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSLog(@"%@", [students valueForKey:@"height"]);
//数组去重distinctUnionOfObjects
NSArray* arr = [students valueForKeyPath:@"@distinctUnionOfObjects.height"];
NSLog(@"arr = %@", arr);
//不去重
NSArray* arr1 = [students valueForKeyPath:@"@unionOfObjects.height"];
NSLog(@"arr1 = %@", arr1);
}
/// 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
- (void) containerNestingTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSMutableArray* students1 = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students1 addObject:student];
}
// 嵌套数组
NSArray* nestArr = @[students, students1];
//去重
NSArray* arr = [nestArr valueForKeyPath:@"@distinctUnionOfArrays.height"];
NSLog(@"arr = %@", arr);
//不去重
NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
NSLog(@"arr1 = %@", arr1);
}
- (void) containerNestingTest1 {
NSMutableSet* students = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSLog(@"students = %@", [students valueForKey:@"height"]);
NSMutableSet* students1 = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
TZPerson* student = [TZPerson new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students1 addObject:student];
}
NSLog(@"students1 = %@", [students1 valueForKey:@"height"]);
NSSet* nestSet = [NSSet setWithObjects:students, students1, nil];
//去重
NSArray* arr1 = [nestSet valueForKeyPath:@"@distinctUnionOfSets.height"];
NSLog(@"arr1 = %@", arr1);
}
KVC集合代理对象
#import <<Foundation/Foundation.h>
@interface TZPerson : NSObject
@property (nonatomic, assign) NSUInteger count;
@property (nonatomic, strong) NSMutableArray *penArr;
@end
#import "TZPerson.h"
@implementation TZPerson
- (NSUInteger) countOfBooks {
return self.count;
}
- (id) objectInBooksAtIndex:(NSUInteger)index {
return [NSString stringWithFormat:@"book %lu", index];
}
// 个数
- (NSUInteger) countOfPens {
return [self.penArr count];
}
// 是否包含这个成员对象
- (id) memberOfPens:(id)object {
return [self.penArr containsObject:object] ? object : nil;
}
// 迭代器
- (id) enumeratorOfPens {
return [self.penArr objectEnumerator];
}
@end
- (void)viewDidLoad {
[super viewDidLoad];
TZPerson* p = [TZPerson new];
p.count = 5;
NSLog(@"books = %@", [p valueForKey:@"books"]);
p.penArr = [NSMutableArray arrayWithObjects:@"pen0", @"pen1", @"pen2", @"pen3", nil];
NSSet* set = [p valueForKey:@"pens"];
NSLog(@"pens = %@", set);
NSEnumerator* enumerator = [set objectEnumerator];
NSString* str = nil;
while (str = [enumerator nextObject]) {
NSLog(@"%@", str);
}
}