一、作用
1.能够对对象的私有成员进行取值赋值
2.对数值和结构体型的属性进行的打包解包处理
二、赋值
赋值过程:
1.先找相关方法 set<Key>:, _set<Key>:, setIs<Key>:
- 若没有相关方法 + (BOOL)accessInstanceVariablesDirectly,判断是否可以直接方法成员变量
- 如果是判断是NO,直接执行KVC的setValue:forUndefinedKey:(系统抛出一个异
常,未定义key) - 如果是YES,继续找相关变量_<key> _is<Key> <key> is<Key>
- 方法或成员都不存在,setValue:
forUndefinedKey:方法,默认是抛出异常
自定义KVC存值
- (void)tz_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];
// 获取方法
Method method = class_getInstanceMethod([self class], NSSelectorFromString(setKey));//实例方法
// class_getClassMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>)//类方法
// 获取参数类型 objc_msgSend(id sel arg)
char *type = method_copyArgumentType(method,2);
// v@:i short long
// 判断类型
if (strcmp(type, "i") == 0) {
NSNumber* num = (NSNumber*)value;
objc_msgSend(self, NSSelectorFromString(setKey), num.intValue);
} else {
objc_msgSend(self, NSSelectorFromString(setKey), value);
}
free(type);
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];
}
// _<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);
}
三、取值
取值过程:
1.先找相关方法 get<Key>, key
- 若没有相关方法 + (BOOL)accessInstanceVariablesDirectly,判断是否可以直接方法成员变量
- 如果是判断是NO,直接执行KVC的valueForUndefinedKey:(系统抛出一个异
常,未定义key) - 如果是YES,继续找相关变量_<key> _is<Key> <key> is<Key>
- 方法或成员都不存在,valueForUndefinedKey:方法,默认是抛出异常
- (nullable id)tz_valueForKey:(NSString *)key {
// 判断是否合法
if (key == nil && key.length == 0) {
return nil;
}
// Key
NSString* Key = key.capitalizedString;
/// 再找相关变量
/// 获取所以成员变量
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]]) {
// id value = object_getIvar(self, ivars[i]);
const char * type = ivar_getTypeEncoding(ivars[i]);
if (strcmp(type, "i")==0) {
int v = ((int (*)(id, Ivar))object_getIvar)(self, ivars[i]);
free(ivars);
return [NSNumber numberWithInt:v];
}
id value = object_getIvar(self, ivars[i]);
free(ivars);
return value;
}
}
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@", Key]]) {
id value = object_getIvar(self, ivars[i]);
free(ivars);
return value;
}
}
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"%@", key]]) {
id value = object_getIvar(self, ivars[i]);
free(ivars);
return value;
}
}
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@", Key]]) {
id value = object_getIvar(self, ivars[i]);
free(ivars);
return value;
}
}
[self valueForUndefinedKey:key];
free(ivars);
return nil;
}
四、KVC异常处理及正确性验证
异常处理:
1.赋值为空 setNilValueForKey:
2.Key值不存在 setValue:forUndefinedKey
异常处理
// 对非对象类型,值不能为空
- (void) setNilValueForKey:(NSString *)key {
NSLog(@"%@ 值不能为空", key);
}
// 赋值key值不存在
- (void) setValue:(id)value forUndefinedKey:(NSString *)key {
NSLog(@"key = %@值不存在 ", key);
}
// 取值key值不存在
- (id) valueForUndefinedKey:(NSString *)key {
NSLog(@"key=%@不存在", key);
return nil;
}
正确性验证:
validateValue
该方法的工作原理:
- 先找一下你的类中是否实现了方法
-(BOOL)validate<Key>:error: - 如果实现了就会根据实现方法里面的自定义逻辑返回NO或者YES,如果没有实现这个方法,则系统默认返回就是YES
TZPerson* p = [TZPerson new];
NSNumber* value = @200;
if ([p validateValue:&value forKey:@"age" error:NULL]) {
[p setValue:value forKey:@"age"];
}
@interface TZPerson : NSObject//.h
@end
@implementation TZPerson//.m
- (BOOL) validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError {
NSNumber* value = (NSNumber*)*ioValue;
NSLog(@"%@", value);
// if (value <= 0 || value >= 200) {
// return NO;
// }
return YES;
}
@end
五、用法
- 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);
/**
输出:
{
age = 18;
name = Tom;
}
*/
}
- KVC的消息传递
/// KVC消息传递 array
- (void) arrayKVCTest {
NSArray* arr = @[@"Monday", @"Tuesday", @"Wednesday"];
NSArray* lengthArr = [arr valueForKey:@"length"];
NSLog(@"%@", lengthArr);
NSArray* lowercaseArr = [arr valueForKey:@"lowercaseString"];
NSLog(@"%@", lowercaseArr);
}
- KVC容器操作
/// 聚合操作符 @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"]);//拿到6个模型中的高
/**
输出:
(
"1.67",
"1.75",
"1.67",
"1.75",
"1.65",
"1.75"
)
*/
/// 平均身高
float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
NSLog(@"%f", avg);//输出:1.706667
}
/// 数组操作符 @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"]);
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);
/** 输出:
arr = (
"1.65",
"1.67",
"1.73",
"1.75",
"1.69"
)
*/
NSArray* arr1 = [nestArr valueForKeyPath:@"@unionOfArrays.height"];
NSLog(@"arr1 = %@", arr1);
/**
输出:
arr1 = (
"1.69",
"1.65",
"1.65",
"1.73",
"1.75",
"1.75",
"1.69",
"1.65",
"1.69",
"1.73",
"1.73",
"1.67"
)
*/
}
//NSMutableSet
- (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];
}
// set使用valueForKey去重,array使用valueForKey不去重
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集合代理对象
TZPerson* p = [TZPerson new];
p.countjhl = 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);
}
@interface TZPerson : NSObject
@property (nonatomic, strong) NSString* name;
@property (nonatomic, assign) int age;
@property (nonatomic, strong) NSString* nick;
@property (nonatomic, assign) float height;
@property (nonatomic, assign) NSUInteger countjhl;
@property (nonatomic, strong) NSMutableArray *penArr;
/** */
@property (nonatomic,copy) NSString *jiahl;
@end
@implementation TZPerson
//Books:.h中未定义的属性
- (NSUInteger) countOfBooks {
return self.countjhl;
}
- (id) objectInBooksAtIndex:(NSUInteger)index {
return [NSString stringWithFormat:@"book %lu", index];
}
//Pens:.h中未定义的属性
// 个数
- (NSUInteger) countOfPens {
return [self.penArr count];
}
// 是否包含这个成员对象
- (id) memberOfPens:(id)object {
return [self.penArr containsObject:object] ? object : nil;
}
// 迭代器
- (id) enumeratorOfPens {
return [self.penArr objectEnumerator];
}
//jiahl:.h中定义的属性
//-(NSUInteger)countOfJiahl
//{
// return self.countjhl;
//}
//
//-(id)objectInJiahlAtIndex:(NSUInteger)index{
// return [NSString stringWithFormat:@"bookjhl %lu", index];
//}
@end
网友评论