成员变量、实例变量、属性
代码一.png
- 成员变量:不会自动生成get、set方法,成员变量不能用点语法调用,因为没有set和get方法,只能使用->调用。
- 属性:属性会自动生成set和get方法,可以使用.语法
- 实例变量:是一种特殊的成员变量,通过class 声明的成员变量
kvc基础使用
@interface QHPerson : NSObject
@property (nonatomic,strong)NSString *name;
@property (nonatomic,strong)QHSon *son;
@end
@interface QHSon : NSObject
@property (nonatomic,strong)NSString *nickName;
@end
QHPerson *p = [QHPerson new];
[p setValue:@"qinhan" forKey:@"name"];
NSString *name = [p valueForKey:@"name"];
QHSon *son = [QHSon new];
[p setValue:son forKeyPath:@"son"];
[p setValue:@"sonName" forKeyPath:@"son.nickName"];
NSString *sonName = [p valueForKeyPath:@"son.nickName"];
如果是要多级访问或读取
- (nullable id)valueForKeyPath:(NSString *)keyPath;
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;
kvc赋值过程
我们先来看一下官网的描述
官网描述1.png
1.先查找
set<Key>
,_set<Key>
两个方法,如果这两个方法都没有实现,执行步骤22.查找
accessInstanceVariablesDirectly
,默认为YES,如果为NO,并且1中的方法也没实现,则会闪退。3.如果accessInstanceVariablesDirectly为YES.则会按照
_<Key>
,_is<Key>
,<key>
,<isKey>
的顺序给成员变量赋值,如果这4个变量名都没找到,则走4的流程4.如果上面的变量都没有,则会走
setValue:forUndefinedKey:
,如果这个方法也没有实现,则会出现异常闪退。我们可以通过这个方法来实现最后的异常处理,防止闪退
kvc取值过程
kvc文档
相比于赋值,取值过程流程相对更加复杂一点
1.先按照get<Key>
,<key>
, is<Key>
, or _<key>
的顺序取值
2.如果第一步的方法都没有找到,则会按照countOf<Key>
,objectIn<Key>AtIndex
,objectIn<Key>AtIndex
,<key> AtIndexes
去找
- 如果找到了其中一个,则会创建一个响应所有
NSArray
方法的集合代理对象,并返回该对象。如果没有则会执行下一步
-这些方法通过对应的.count
、array[index]
、objectsAtIndexes
,objectAtIndex
访问到,前提是必须要实现对应的方法,否则会闪退
3.如果2都没有实现,查找名为countOf<Key>
、enumeratorOf<Key>
和memberOf<Key>
这三个方法(对应于NSSet
类定义的原始方法)
4.如果3都没有找到,则会调用accessInstanceVariablesDirectly
,如果为YES
,会按照顺序取值,都没有就会闪退。如果为NO
,就会跳到6
5.判断取出的属性值,如果为是对象就会直接返回,如果不是会转成NSNumber
返回
-
valueForUndefinedKey
,如果走到这一步没有实现这个方法,则会异常,所以当我们自定义kvc的时候,最好实现这个方法
下面附上nsarray代码
@interface QHArray ()
@property (nonatomic,readwrite,assign) NSUInteger count;
@end
@implementation QHArray
//-(void)incrementCount{
// NSLog(@"%s",__func__);
// self.count ++;
//}
-(NSUInteger)countOfNumbers{
NSLog(@"%s",__func__);
return self.count;
}
-(id)objectInNumbersAtIndex:(NSUInteger)index{
//当key使用numbers时,KVC会找到这两个方法。
NSLog(@"%s",__func__);
return @(index * 2);
}
- (NSArray *)numbersAtIndexes:(NSIndexSet *)indexes
{
NSLog(@"%s",__func__);
return @[@"1",@"2"];
}
-(NSInteger)getNum{
NSLog(@"%s",__func__);//第一个,自己一个一个注释试
return 10;
}
-(NSInteger)num{
NSLog(@"%s",__func__);//第二个
return 11;
}
-(NSInteger)isNum{
NSLog(@"%s",__func__);//第三个
return 12;
}
//实现
QHArray *arr = [QHArray new];
NSNumber* num = [arr valueForKey:@"num"];
NSLog(@"%@",num);
id ar = [arr valueForKey:@"numbers"];
NSLog(@"ar:%@",ar);
NSLog(@"0:%@ 1:%@",ar[0],ar[1]);
NSIndexSet *sets = [NSIndexSet indexSetWithIndex:1];
NSLog(@"sets:%@", [ar objectsAtIndexes:sets]);
kvc应用场景
1.动态的赋值和取值
setValue:forKey:
、valueForKey:
还有路由setValue:forKeyPath:
,valueForKeyPath:
2.多值操作(model和字典互转)
结合runtime,获取成员变量的值,可以实现model和字典转换
3.用KVC实现高阶消息传递
//KVC实现高阶消息传递
- (void)transmitMsg{
NSArray *arrStr = @[@"english", @"franch", @"chinese"];
NSArray *arrCapStr = [arrStr valueForKey:@"capitalizedString"];
for (NSString *str in arrCapStr) {
NSLog(@"%@", str);
}
NSArray *arrCapStrLength = [arrCapStr valueForKeyPath:@"capitalizedString.length"];
for (NSNumber *length in arrCapStrLength) {
NSLog(@"%ld", (long)length.integerValue);
}
}