网站建设策划解决方案,wordpress菲插件关键词,网站制作多少钱新闻,东莞哪里有网站制作公司【iOS】KVC的学习 文章目录 【iOS】KVC的学习前言KVC定义KVC设值KVC取值KVC使用keyPathKVC处理异常处理nil异常 KVC的一些应用修改动态的设置值实现高阶的消息传递 小结 前言 笔者简单学习了有关与KVC的相关内容#xff0c;这里写一篇博客简单介绍一下相关内容。 KVC
定义 KV…【iOS】KVC的学习 文章目录 【iOS】KVC的学习前言KVC定义KVC设值KVC取值KVC使用keyPathKVC处理异常处理nil异常 KVC的一些应用修改动态的设置值实现高阶的消息传递 小结 前言 笔者简单学习了有关与KVC的相关内容这里写一篇博客简单介绍一下相关内容。 KVC
定义 KVCKey-value coding键值编码就是指iOS的开发中可以允许开发者通过Key名直接访问对象的属性或者给对象的属性赋值。而不需要调用明确的存取方法。这样就可以在运行时动态地访问和修改对象的属性。而不是在编译时确定这也是iOS开发中的黑魔法之一。很多高级的iOS开发技巧都是基于KVC实现的。 KVC的定义是通过对于NSObject的扩展来实现的下面是几个有关于KVC最重要的四个方法
- (nullable id)valueForKey:(NSString *)key; //直接通过Key来取值- (void)setValue:(nullable id)value forKey:(NSString *)key; //通过Key来设值- (nullable id)valueForKeyPath:(NSString *)keyPath; //通过KeyPath来取值- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath; //通过KeyPath来设值
这里我们从KVC设置值开始说起
KVC设值
首先我们要清楚KVC是怎么实现一个设置值的效果的这里我们举一个例子[test setValue:1223 forKey:str];这条语句在执行的时候KVC到底执行了那些代码。
程序优先调用set:属性值方法代码通过setter方法完成设置。注意这里的是指成员变量名首字母大小写要符合KVC的命名规则下同如果没有找到setName方法KVC机制会检查 (BOOL)accessInstanceVariablesDirectly方法有没有返回YES默认该方法会返回YES如果你重写了该方法让其返回NO的话那么在这一步KVC会执行setValueforUndefinedKey方法不过一般开发者不会这么做。所以KVC机制会搜索该类里面有没有名为**_str** 的成员变量无论该变量是在类接口处定义还是在类实现处定义也无论用了什么样的访问修饰符只在存在以_命名的变量KVC都可以对该成员变量赋值。如果该类即没有set方法也没有_成员变量KVC机制会搜索isStr的成员变量。
所以简而言之就是这个设置值的函数查找成员变量的顺序就是顺序查找名称类似于 _key、_isKey、key 或 isKey 的实例变量。只要能找到一个满足条件的成员变量就会给这个成员变量设置我们的一个值下面给一段代码来讲解一下。
#import Foundation/Foundation.hNS_ASSUME_NONNULL_BEGINinterface TestObject : NSObject {publicNSString* isAge;//NSString* _age;
}
property NSString* str;
property NSArray* ary;
endNS_ASSUME_NONNULL_END
#import TestObject.himplementation TestObject
end#import Foundation/Foundation.h
#import TestObject.h
int main(int argc, const char * argv[]) {autoreleasepool {// insert code here...TestObject* test [[TestObject alloc] init];[test setValue:[] forKey:ary];[test setValue:1223 forKey:str];[test setValue:12 forKey:age];NSLog(%, test-isAge);//NSLog(%, [test valueForKey:_age]);}return 0;
}打印结果 12 Type: Notice | Timestamp: 2024-09-17 16:31:02.78447108:00 | Process: KVC | Library: KVC | TID: 0xda0dbb 可以看到上面这段代码虽然我们不是一个名字为age的标题但是还是能打印出一个结果。
然后我们修改一下成员变量部分
interface TestObject : NSObject {publicNSString* isAge;NSString* _age;
}主函数
int main(int argc, const char * argv[]) {autoreleasepool {// insert code here...TestObject* test [[TestObject alloc] init];[test setValue:[] forKey:ary];[test setValue:1223 forKey:str];[test setValue:12 forKey:age];NSLog(%, test-isAge);NSLog(%, test-_age);//NSLog(%, [test valueForKey:_age]);NSLog(%, test.str);NSLog(%, test.ary);}return 0;
}(null) Type: Notice | Timestamp: 2024-09-17 17:14:26.41662808:00 | Process: KVC | Library: KVC | TID: 0xda5d69 12 Type: Notice | Timestamp: 2024-09-17 17:14:26.41667008:00 | Process: KVC | Library: KVC | TID: 0xda5d69 可以看到我们的isAge这个变量打印出了一个null而我们下面的age这个变量则打印出来一个12也就是我们想要的值这里就说明KVC方法的取值是有一个固定的顺序的。也就是我们上面提到的 _key、_isKey、key 或 isKey 。
同时我们这里如果不想让我们的一个代码实现一个KVC的话我们可以通过设置 (BOOL)accessInstanceVariablesDirectly这种方法让他永远返回NO这样就可以让这个类无法使用KVC。我们先重写下面几个方法,主函数仍旧是上面那一个主函数。 (BOOL)accessInstanceVariablesDirectly {return NO;
}
- (id)valueForUndefinedKey:(NSString *)key {NSLog(出现异常);return nil;
}
- (void)setValue:(id)value forKey:(NSString *)key {NSLog(出现异常无法设置);
}打印结果 出现异常无法设置。 Type: Notice | Timestamp: 2024-09-17 18:47:05.23521808:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd 出现异常无法设置 Type: Notice | Timestamp: 2024-09-17 18:47:05.23528508:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd 出现异常无法设置 Type: Notice | Timestamp: 2024-09-17 18:47:05.23528508:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd (null) Type: Notice | Timestamp: 2024-09-17 18:47:05.23532608:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd (null) Type: Notice | Timestamp: 2024-09-17 18:47:05.23532608:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd (null) Type: Notice | Timestamp: 2024-09-17 18:47:05.23532608:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd (null) Type: Notice | Timestamp: 2024-09-17 18:47:05.23534008:00 | Process: KVC | Library: KVC | TID: 0xdaa7bd 重写这个方法后如果我们的KVC找不到set Key 这个方法之后就不会继续向下寻找了。 正如上图所示KVC的设值大概就是上图的过程。
KVC取值
有关于KVC取值一般采用
首先按get Key key,is Key的顺序方法查找getter方法找到的话会直接调用。如果是BOOL或者Int等值类型 会将其包装成一个NSNumber对象。如果还没有找到再检查类方法 (BOOL)accessInstanceVariablesDirectly,如果返回YES(默认行为)那么和先前的设值一样会按_,_is,is的顺序搜索成员变量名这里不推荐这么做因为这样直接访问实例变量破坏了封装性使代码更脆弱。
一般情况按照这三个顺序进行一个查找笔者这里给出函数。
#import TestObject.himplementation TestObject
-(int)getAge {return 1222;
}
-(int)age {return 120;
}
-(int)isAge {return 10;
}
end
#import Foundation/Foundation.h
#import TestObject.h
int main(int argc, const char * argv[]) {autoreleasepool {// insert code here...TestObject* test [[TestObject alloc] init];[test setValue:[] forKey:ary];[test setValue:1223 forKey:str];[test setValue:12 forKey:age];NSLog(%,[test valueForKey:age]);//NSLog(%, test-_age);//NSLog(%, [test valueForKey:_age]);NSLog(%, test.str);//NSLog(%, test.ary);}return 0;
}打印结果 1222 Type: Notice | Timestamp: 2024-09-18 22:58:51.53673208:00 | Process: KVC | Library: KVC | TID: 0xdf2afb 返回的是我们getAge的方法返回的函数。
这时候我们注释getAge然后重新运行一下 120 Type: Notice | Timestamp: 2024-09-18 23:00:38.28568108:00 | Process: KVC | Library: KVC | TID: 0xdf33e6 最后我们在注释age在运行一下得到 10 Type: Notice | Timestamp: 2024-09-18 23:02:06.66876708:00 | Process: KVC | Library: KVC | TID: 0xdf3d01 最后我们注释isAge运行一下可以得到。 12 Type: Notice | Timestamp: 2024-09-18 23:05:09.89744308:00 | Process: KVC | Library: KVC | TID: 0xdf4c04 KVC使用keyPath
有时候我们的要改变的对象可能是比较复杂的比如说自定义类或者是其他复杂的数据类型我们如果使用key来一层一层的监控的话会非常复杂这时候就出现了keyPath这个方法来简化代码。
如下面的代码我们先定义一个新的类。
#import Foundation/Foundation.h
#import TestObject.h
NS_ASSUME_NONNULL_BEGINinterface NextText : NSObject
property TestObject* test;
endNS_ASSUME_NONNULL_END上面这个自定义类存储了一个属性是我们之前创建的对象。
int main(int argc, const char * argv[]) {autoreleasepool {// insert code here...TestObject* test [[TestObject alloc] init];NextText* test1 [[NextText alloc] init];[test1 setValue:test forKey:test];[test1 setValue:1222 forKeyPath:test.str];NSLog(%, [test1 valueForKeyPath:test.str]);}return 0;
}打印结果 1222 Type: Notice | Timestamp: 2024-09-19 18:24:49.99858008:00 | Process: KVC | Library: KVC | TID: 0xe07e3c 这里可以看到我们在这里也是成功设置了有关keyPath的内容然后也通过keyPath来实现了获取对应的数值。所以说我们仅仅需要用一个点语法的形式就可以实现一个修改对应路径的一个值。
KVC处理异常
在一般情况下我们是不允许KVC来给一个对象赋值为nil的这时候我们可能需要自己去处理一下nil异常的部分。比方说我们传入一个nil给NSInteger的对象的时候就会出现一个异常的问题会执行-(void)setNilValueForKey:(NSString *)key这个方法这个方法会产生一个Crash所以我们通常需要重写这个方法。
处理nil异常
#import Foundation/Foundation.hNS_ASSUME_NONNULL_BEGINinterface TestObject : NSObject {publicNSInteger _age;
}
property NSString* str;
property NSArray* ary;
endNS_ASSUME_NONNULL_END
#import TestObject.himplementation TestObject
- (void)setNilValueForKey:(NSString *)key {if ([key isEqualToString:age]) {NSLog(不能设置成nil状态%, key);_age 11;} else {[super setNilValueForKey:key];}
}
end
#import Foundation/Foundation.h
#import TestObject.h
#import NextText.h
int main(int argc, const char * argv[]) {autoreleasepool {// insert code here...TestObject* test [[TestObject alloc] init];[test setValue:nil forKey:age];NSLog(%, [test valueForKey:age]);//NSLog(%, test.ary);}return 0;
}
然后执行代码的打印结果是 不能设置成nil状态age Type: Notice | Timestamp: 2024-09-19 19:31:52.43413408:00 | Process: KVC | Library: KVC | TID: 0xe16087 11 Type: Notice | Timestamp: 2024-09-19 19:31:52.43430508:00 | Process: KVC | Library: KVC | TID: 0xe16087 Program ended with exit code: 0 Type: stdio KVC的一些应用
修改动态的设置值
这是KVC最简单的应用。
实现高阶的消息传递
首先我们要明白对于容器使用一个KVC并没有对于我们的容器进行一个操作实际上是将这个方法传递给容器中的每一个元素然后再重新返回一个容器所以这里我们实现一些特殊的效果,通过操作一个容器来返回一个符合我们需求的容器。
int main(int argc, const char * argv[]) {autoreleasepool {// insert code here...NSArray* arrStr [english,franch,chinese];NSArray* arrCapStr [arrStr valueForKey:capitalizedString];for (NSString* str in arrCapStr) {NSLog(%,str);}NSArray* arrCapStrLength [arrStr valueForKeyPath:capitalizedString.length];for (NSNumber* length in arrCapStrLength) {NSLog(%ld,(long)length.integerValue);}//NSLog(%, test.ary);}return 0;
}打印结果 English Type: Notice | Timestamp: 2024-09-19 19:52:26.63807508:00 | Process: KVC | Library: KVC | TID: 0xe198e4 Franch Type: Notice | Timestamp: 2024-09-19 19:52:26.63813008:00 | Process: KVC | Library: KVC | TID: 0xe198e4 Chinese Type: Notice | Timestamp: 2024-09-19 19:52:26.63815008:00 | Process: KVC | Library: KVC | TID: 0xe198e4 7 Type: Notice | Timestamp: 2024-09-19 19:52:26.63886508:00 | Process: KVC | Library: KVC | TID: 0xe198e4 6 Type: Notice | Timestamp: 2024-09-19 19:52:26.63889508:00 | Process: KVC | Library: KVC | TID: 0xe198e4 7 Type: Notice | Timestamp: 2024-09-19 19:52:26.63891208:00 | Process: KVC | Library: KVC | TID: 0xe198e4 Program ended with exit code: 0 Type: stdio 可以看到这里我们通过KVC来实现了一个让容器中所有字符串第一个字符大写的容器。
小结 笔者在这里简单介绍了一下有关于KVC的使用之后还会继续学习相关的内容。