KVC

weixin_34413065發表於2018-06-14

原理:

  • valueForKey:
    1.訪問器匹配:先尋找與key,isKey, getKey (實測還有_key)同名的方法,返回值為物件型別。
    2.例項變數匹配:尋找與key, _key,isKey,_isKey同名的例項變數
  • setValueForKey:
    1.存取器匹配:先尋找與setKey同名的方法,且引數要為一個物件型別
    2.例項變數匹配:尋找與key,_isKey,_key,isKey同名的例項變數,直接賦值。

集合操作

  1. 獲取陣列中最大的值
NSArray *a = @[@4, @84, @2];
NSLog(@"max = %@", [a valueForKeyPath:@"@max.self"]);
  1. 獲取陣列中物件屬性的最大值
Transition *t1 = [[Transition alloc] init];
    t1.amount = 10;
    Transition *t2 = [[Transition alloc] init];
    t2.amount = 20;
    Transition *t3 = [[Transition alloc] init];
    t3.amount = 30;
    
    NSArray *a = @[t1, t2, t3];
    NSLog(@"max = %@", [a valueForKeyPath:@"@max.amount"]);

還有 @max @min @avg @count @sum
@max和@min在進行判斷時,都是通過呼叫compare:方法進行判斷,所以可以通過重寫該方法對判斷過程進行控制。

字典轉模型

TYLModel 模型

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *icon;
    NSDictionary *dic = @{@"name": @"James", @"icon": @"33.png"};
    TYLModel *model = [[TYLModel alloc] init];
    [model setValuesForKeysWithDictionary:dic];
    NSLog(@"name = %@\n icon = %@", model.name, model.icon);

內部實現原理

NSDictionary *dic = @{@"name": @"James", @"icon": @"33.png"};
TYLModel *model = [[TYLModel alloc] init];
[dic enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        [model setValue:obj forKey:key];
 }];
NSLog(@"name = %@\n icon = %@", model.name, model.icon);

修改系統控制元件內部屬性(runtime + KVC) 參考

eg: UIPageControl 圖片修改 runtime 遍歷出屬性, 修改圖片

UIPageControl *pageControl = [[UIPageControl alloc] init]; 
 [pageControl setValue:[UIImage imageNamed:@"home_slipt_nor"] forKeyPath:@"_pageImage"];
 [pageControl setValue:[UIImage imageNamed:@"home_slipt_pre"] forKeyPath:@"_currentPageImage"];

一些技巧

  1. 伺服器返回 如 'id' 欄位, 關鍵字, 或者找不到key的時候
- (nullable id)valueForUndefinedKey:(NSString *)key;
- (void)setValue:(nullable id)value forUndefinedKey:(NSString *)key;
  1. 當通過KVC給某個非物件的屬性賦值為nil時
    例如給name賦值為nil的時候,就可以重寫setNilValueForKey:方法並表示name是空的。
- (void)setNilValueForKey:(NSString *)key {
    if ([key isEqualToString:@"name"]) {
        [self setValue:@"" forKey:@”age”];
    } else {
        [super setNilValueForKey:key];
    }
}