iOS基础全面分析之一(KVC全面分析)
iOS基础全面分析之二(RunLoop全面分析)
iOS基础全面分析之三(KVO全面分析)
KVC全面分析
基本使用
- 能够对对象的私有成员进行取值赋值
- 对数值和结构体的属性进行的打包解包处理 (NSNumber、NSValue)
基本赋值过程
- 先找相关方法:
set
:, _set :, setIs : - 若没有相关方法,会调用
+ (BOOL)accessInstanceVariablesDirectly
方法 - 如果return是NO,直接执行KVC的
[object setValue:forUndefinedKey:]:
(系统抛出一个异常,未定义的key) -
+ (BOOL)accessInstanceVariablesDirectly
如果return是YES,继续找相关的变量:-
相关变量赋值顺序,
//_
,_is , ,is // NSString * name; //3 // NSString * _name; //1 // NSString * isName; //4 // NSString * _isName; //2
-
- 如果方法或成员变量都不存在,
[object setValue:forUndefinedKey:]:
(系统抛出一个异常,未定义的key)
基本取值过程
- 先找相关方法
get
, , ,_ - 若没有相关方法,会调用
+ (BOOL)accessInstanceVariablesDirectly
方法 - 如果return是NO,执行KVC的
[object valueForUndefinedKey:]:
(系统抛出一个异常,未定义的key) -
+ (BOOL)accessInstanceVariablesDirectly
如果return是YES,继续找相关的变量:-
相关变量取值顺序
//_
,_is , ,is // NSString * name; //3 // NSString * _name; //1 // NSString * isName; //4 // NSString * _isName; //2
-
- 方法根成员变量都不存在,执行KVC的
[object valueForUndefinedKey:]:
(系统抛出一个异常,未定义的key)
KVC自定义
set方法自定义
- (void)tz_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);
}
get方法自定义
- (NSString *)GG_getValueforKey:(NSString *)key{
// 判断是否合法
if (key == nil && key.length == 0) {
NSException* exception = [NSException exceptionWithName:@"崩溃" reason:@"value不合法" userInfo:nil];
@throw exception;
}
// Key
NSString* CapitalKey = key.capitalizedString;
//get,
NSString* getKey = [NSString stringWithFormat:@"get%@", CapitalKey];
if ([self respondsToSelector:NSSelectorFromString(getKey)]) {
return [self performSelector:NSSelectorFromString(getKey)];
}
NSString* Key = [NSString stringWithFormat:@"%@", key];
if ([self respondsToSelector:NSSelectorFromString(Key)]) {
return [self performSelector:NSSelectorFromString(Key)];
}
if (![self.class accessInstanceVariablesDirectly]) {
NSException* exception = [NSException exceptionWithName:@"NSUnkonwnKeyException" reason:@"valueForUndefinedKey:" userInfo:nil];
@throw exception;
}
#pragma mark -- 获取所以成员变量
/// 再找相关变量
/// 获取所以成员变量
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];
NSLog(@"%@",name);
[arr addObject:name];
}
// _ _is is
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@", key]]) {
NSString * var = object_getIvar(self, ivars[i]);
free(ivars);
return var;
}
}
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@", CapitalKey]]) {
NSString * var = object_getIvar(self, ivars[i]);
free(ivars);
return var;
}
}
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"%@", key]]) {
NSString * var = object_getIvar(self, ivars[i]);
free(ivars);
return var;
}
}
for (int i = 0; i < count; i++) {
NSString* keyName = arr[i];
if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@", CapitalKey]]) {
NSString * var = object_getIvar(self, ivars[i]);
free(ivars);
return var;
}
}
NSString *RNstr = [self valueForUndefinedKey:key];
free(ivars);
return RNstr;
}
KVC异常处理及正确性验证
非对象类型,KVC的异常处理
@interface Person : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
@end
Person *per = [[Person alloc]init];
//这里会崩溃,因为下面是一个值类型,而值类型不能为nil,所以会崩溃
/*
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '[ setNilValueForKey]: could not set nil as the value for the key age.'
*/
[per setValue:nil forKey:@"age"];
上述是值类型用KVC赋值为nil崩溃,怎样才能不崩溃呐?
需要在Person
重写setNilValueForKey
方法
//非对象类型,不能为nil
- (void)setNilValueForKey:(NSString *)key{
NSLog(@"%@,值不能为空",key);
}
取值和赋值key不存在的异常捕获
//赋值异常捕获
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
NSLog(@"key == %@ 值不存在",key);
}
//取值的异常捕获
- (id)valueForUndefinedKey:(NSString *)key{
NSLog(@"key == %@ 值不存在",key);
return @"";
}
扩展:万能容器
- (void)setValue:(id)value forUndefinedKey:(NSString *)key{
if(!key || [key isEqualToString:@""]){
Persondict = [NSMutableDictionary dictionary];
}
[Persondict setValue:value forUndefinedKey:key];
}
- (id)valueForUndefinedKey:(NSString *)key{
if(!key || [key isEqualToString:@""]){
return nil;
}
return [Persondict objectForKey:key];
}
value正确性验证
- (BOOL)validateValue: forKey: error:,- (BOOL)validateValue: forKeyPath: error:
,这两个方法是KVC提供的验证值的正确性的方法
- 值所对应的验证方法是否实现,- (BOOL)validate
: forKey: error: - 如果实现了就会根据实现方法里面的自定义逻辑返回YES和NO,如果没有实现这个方法,则默认系统会返回YES
例子:存储一个Int
类型age,这个值必须0
person
对象。如果符合就赋值,代码如下:
@implementation Person
- (BOOL)validateAge:(inout id _Nullable __autoreleasing *)ioValue error:(out NSError * _Nullable __autoreleasing *)outError{
NSInteger value = [(NSNumber *) * ioValue integerValue];
if(value <=0 || value>=200){
return NO;
}
return YES;
}
@end
//正确性验证
NSNumber *value = @200;
Person *per = [[Person alloc]init];
if([per validateValue:&value forKey:@"age" error:NULL]){
[per setValue:value forKey:@"age"];
}
KVC进阶使用
KVC与字典
字典转模型
通过一个字典,以字典Key为属性名称直接赋值给model
,一种字典转模型的模式
@interface Person : NSObject
@property(nonatomic,copy)NSString *name;
@property(nonatomic,assign)NSInteger age;
@property(nonatomic,copy)NSString *nick;
@property(nonatomic,assign)float height;
@end
NSDictionary *dict = @{
@"name":@"struggle3g",
@"age":@18,
@"nick":@"cat",
@"height":@170
};
Person *per = [[Person alloc]init];
[per setValuesForKeysWithDictionary:dict];
NSLog(@"per.name = %@, per.age = %d, per.nick =%@, per.height = %f", per.name, per.age, per.nick, per.height);
通过一组key获取字典
NSArray* keys = @[@"name", @"age"];
NSDictionary* dict1 = [p dictionaryWithValuesForKeys:keys];
NSLog(@"%@", dict1);
NSArray使用valueForKey:向所有的成员发送消息 (KVC消息传递)
- (void)arrayKVCtext{
//向这个数组里面的所有方法发送消息
NSArray *arr = @[@"Mondy",@"Tuesday",@"Wednesday"];
NSArray *lengtharr = [arr valueForKey:@"length"];
NSLog(@"%@",lengtharr);
}
KVC容器操作
聚合操作
/*聚合操作*/
NSMutableArray *students = [[NSMutableArray alloc]init];
for (int i = 0; i<6; i++) {
Person *per = [Person new];
NSDictionary *dict = @{
@"name":@"struggle3g",
@"age":@(18+i),
@"nick":@"cat",
@"height":@(1.70+0.02*arc4random_uniform(6))
};
[per setValuesForKeysWithDictionary:dict];
[students addObject:per];
}
NSLog(@"%@",[students valueForKey:@"height"]);
//平均身高
/*
//聚合的公式,这个在下面官网有注解 https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/KeyValueCoding/CollectionOperators.html#//apple_ref/doc/uid/20002176-SW5
*/
float avg = [[students valueForKeyPath:@"@avg.height"] floatValue];
NSLog(@"%f",avg);
数组操作
- (void)arrayKVCtext{
//向这个数组里面的所有方法发送消息
NSArray *arr = @[@"Mondy",@"Tuesday",@"Wednesday"];
NSArray *lengtharr = [arr valueForKey:@"length"];
NSLog(@"%@",lengtharr);
}
嵌套操作(array&set)操作
嵌套操作array
/// 嵌套集合(array&set)操作 @distinctUnionOfArrays @unionOfArrays @distinctUnionOfSets
- (void) containerNestingTest {
NSMutableArray* students = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
Person* student = [Person 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++) {
Person* student = [Person 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);
}
嵌套操作set
//无序的嵌套集合
- (void) containerNestingTest1{
NSMutableSet* students = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
Person* student = [Person new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students addObject:student];
}
NSMutableSet* students1 = [NSMutableSet set];
for (int i = 0; i < 6; i++) {
Person* student = [Person new];
NSDictionary* dict = @{
@"name":@"Tom",
@"age":@(18+i),
@"nick":@"Cat",
@"height":@(1.65 + 0.02*arc4random_uniform(6)),
};
[student setValuesForKeysWithDictionary:dict];
[students1 addObject:student];
}
//set这个无序集合,向每一个成员发送消息,返回的值是默认是去重的
NSLog(@"students = %@",[students valueForKey:@"height"]);
NSLog(@"students1 = %@",[students1 valueForKey:@"height"]);
NSSet *set = [NSSet setWithObjects:students,students1, nil];
NSArray *arr1 = [set valueForKeyPath:@"@distinctUnionOfSets.height"];
NSLog(@"%@",arr1);
}
集合代理对象
Array的集合代理对象
@interface Person : NSObject
@property (nonatomic,assign)NSUInteger count;
@end
@implementation Person
#pragma mark -- 集合代理对象
- (NSUInteger)countOfBooks{
return self.count;
}
-(id)objectInBooksAtIndex:(NSUInteger)index{
return [NSString stringWithFormat:@"book %lu",index];
}
@end
/*array集合代理对象 一对多的关系 array是2个方法*/
Person *p = [[Person alloc]init];
p.count = 5;
NSLog(@"books = %@",[p valueForKey:@"books"]);
Set的集合代理对象
@interface Person : NSObject
@property (nonatomic,strong)NSMutableArray *penArr;
@end
@implementation Person
#pragma mark -- 集合代理对象
//个数
- (NSUInteger)countOfPens{
return [self.penArr count];
}
//是否包含这个成员对象
- (id)memberOfPens:(id)object{
return [self.penArr containsObject:object]?object:nil;
}
//迭代器
-(id)enumeratorOfPens{
return [self.penArr reverseObjectEnumerator];
}
@end
/*set集合代理对象 set是3个方法*/
p.penArr = [NSMutableArray arrayWithObjects:@"pen0",@"pen1",@"pen2", nil];
NSSet *set = [p valueForKey:@"pens"];
NSLog(@"%@",set);