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

相關文章
相關標籤/搜索