KVC

原文连接数组

Key-value coding is a mechanism enabled by the NSKeyValueCoding informal protocol that objects adopt to provide indirect access to their properties. When an object is key-value coding compliant, its properties are addressable via string parameters through a concise, uniform messaging interface. This indirect access mechanism supplements the direct access afforded by instance variables and their associated accessor methods.app

以上是Apple对KVC的定义,翻译过来就是:ide

键值编码是由NSKeyValueCoding非正式协议启用的机制,对象采用该机制提供对其属性的间接访问。 当对象符合键值编码时,其属性可经过字符串参数经过简洁,统一的消息传递接口寻址。 这种间接访问机制补充了实例变量及其相关访问器方法提供的直接访问。ui

全部直接或者间接继承NSObject的对象都遵照 NSKeyValueCoding 协议,并默认提供实现,找到 NSKeyValueCoding.h 咱们能够看到里面为这些类添加了一些extensionthis

@interface NSObject(NSKeyValueCoding)

@interface NSArray<ObjectType>(NSKeyValueCoding)

@interface NSDictionary<KeyType, ObjectType>(NSKeyValueCoding)

@interface NSMutableDictionary<KeyType, ObjectType>(NSKeyValueCoding)

@interface NSOrderedSet<ObjectType>(NSKeyValueCoding)

@interface NSSet<ObjectType>(NSKeyValueCoding)

复制代码

下面是一些主要的方法:编码

- (nullable id)valueForKey:(NSString *)key;

- (void)setValue:(nullable id)value forKey:(NSString *)key;

- (nullable id)valueForKeyPath:(NSString *)keyPath;
  
- (void)setValue:(nullable id)value forKeyPath:(NSString *)keyPath;

- (nullable id)valueForUndefinedKey:(NSString *)key;

- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;

复制代码

实践是检验真理的惟一标准,下面建立一个ComandLine工程实践一下atom

  • Xcode -> New -> MacOS -> CommandLine
  • 建立Person类

关于KVC要分为两部分,集合类型与非集合类型,下面先来看非集合类型spa

Person.h翻译

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface Person : NSObject

@property (nonatomic ,copy) NSString *name;

@property (nonatomic ,assign) NSUInteger age;

@property (nonatomic ,copy) NSArray<Person *> *friends;

@end

NS_ASSUME_NONNULL_END

复制代码

Person.m3d

#import "Person.h"

@implementation Person

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        _name = @"";
        _age = 0;
        _friends = @[];
    }
    
    return self;
}

// 重写以便打印对象的属性
- (NSString *)description {
    
    return [NSString stringWithFormat:@"- name: %@, age: %ld, friends: %@",self.name, self.age, self.friends];
}

@end
复制代码

打开main.m,建立两个Person的实例Alice和Bob

#import <Foundation/Foundation.h>
#import "Person.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        
        Person *Alice = Person.new;
        Alice.name = @"Alice";
        Alice.age = 18;
        Alice.friends = @[];
        
        Person *Bob = Person.new;
        [Bob setValue:@"Bob" forKey:@"name"];
        [Bob setValue:@(28) forKey:@"age"];
        [Bob setValue:@[Alice] forKey:@"friends"];
        
        NSLog(@"%@",@[Alice,Bob]);
        NSLog(@"%@",[Bob valueForKeyPath:@"friends"]);
    }
    return 0;
}

复制代码

其中,ALice使用正常的set访问器进行赋值,Bob使用KVC方式赋值, Command + R,可以看到以下输出

(
    "- name: Alice, age: 18, friends: (\n)",
    "- name: Bob, age: 28, friends: (\n \"- name: Alice, age: 18, friends: (\\n)\"\n)"
)

(
    "- name: Alice, age: 18, friends: (\n)"
)
复制代码

输出正如咱们想的那样,经过KVC,正确的对Bob的name,age,friends进行了赋值和取值操做

那么若果想直接获取Bob名字的长度呢

id nameLength = [Bob valueForKeyPath:@"name.length"];
复制代码

有没有发现这种方式很便捷呢,后面还有更有趣的用法。

接下来想一个问题,若是我想使用KVC进行赋值可是相应的类并无对应的属性怎么办

[Bob setValue:@"Bob" forKey:@"familyName"];
复制代码

运行就会崩溃掉,而在生产环境的状况下咱们可不想让App Crash, 能够看下报错信息

Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x1030063b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key familyName.'
复制代码

能够发现致使崩溃的缘由是Person类没有实现这个方法 setValue:forUndefinedKey: 接下来在Person.m里面添加这个方法的实现

- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    
}
复制代码

能够看到运行以后不会崩溃了

上面简单介绍了一下非集合类型,下面先来看集合类型

首先new一个Array

NSArray *family = @[Alice,Bob];
NSLog(@"%@",[family valueForKey:@"count"]);
复制代码

运行后发现又又又crash了,

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x100703db0> valueForUndefinedKey:]: this class is not key value coding-compliant for the key count.'
复制代码

按照正常的理解,咱们无非是想回去Array的count属性,为何会崩溃呢,原来对集合类型进行KVC操做时是分别对集合内对象进行操做,

NSLog(@"%@",[family valueForKey:@"name"]);
复制代码

如今的输出是一个Array

(
    Alice,
    Bob
)
复制代码

使用KVC对集合类型内部对象赋值也是同样的

NSArray *family = @[Alice,Bob];
[family setValue:@"NoName" forKey:@"name"];
NSLog(@"%@",family);
复制代码

固然,你也能够本身重写上述的方法来使用,这里就很少赘述。

下面来还有一些更有趣的

KVC集合操做符

image

能够看到对于集合操做,在keypath中间咱们能够加入一个集合操做符,可以实现更为便捷的骚操做

集合操做符有一下几种

  • @avg // 算数平均值
  • @count // 计数
  • @max // 最大值
  • @min // 最小值
  • @sum // 求和
  • @unionOfObjects // 建立并返回一个数组,该数组包含与右键路径指定的属性对应的集合的全部对象。
  • @distinctUnionOfObjects // 同unionOfObjects,但会对数组去重
  • @unionOfArrays // 建立并返回一个数组,该数组包含与右键路径指定的属性对应的全部集合的组合的对象。
  • @distinctUnionOfArrays // 同unionOfArrays,但会对数组去重
NSLog(@"%@",[family valueForKeyPath:@"@max.age"]);
NSLog(@"%@",[family valueForKeyPath:@"@min.age"]);
NSLog(@"%@",[family valueForKeyPath:@"@sum.age"]);
NSLog(@"%@",[family valueForKeyPath:@"@avg.age"]);
复制代码

这里能够看到获取到了Alice和Bob年龄的最大值,最小值,和,平均值

还有一种特殊场景,就是我想获取这个family的全部friends怎么作呢

NSLog(@"%@",[family valueForKeyPath:@"@unionOfObjects.friends"]);

(
        (
    ),
        (
        "- name: Alice, age: 18, friends: (\n)"
    )
)
复制代码

能够看到打印彷佛有些不对劲,咱们想要的是全部的friends,但数组中倒是Alice的friends,Bob的friends两个对象。 这时候咱们可使用unionOfArrays,这个操做符会帮咱们将数组元素【铺平】

NSLog(@"%@",[family valueForKeyPath:@"@unionOfArrays.friends"]);
复制代码

一句代码即展开获取了你所须要的Array,不然你只能写一些胶水代码

NSArray *arr = [Alice.friends arrayByAddingObjectsFromArray:Bob.friends];
复制代码

不够优雅

基本的KVC就介绍这么多,若是有更生层次的自定义实现需求的话仍是推荐多看看Apple的文档,不过记得使用KVC的话当对象更新了property不要忘记顺便也要改下keypath

相关文章
相关标签/搜索