一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

復(fù)習(xí)一下KVC

 丹楓無跡 2022-01-11

一. 前言

KVC(Key Value Coding)是Cocoa框架為開發(fā)者提供的非常強(qiáng)大的工具,簡單解釋為:鍵值編碼。它依賴于Runtime,在OC的動(dòng)態(tài)性方面發(fā)揮了重要作用。
它主要的功能在于直接通過變量名稱字符串來訪問成員變量,不管是私有的還是共有的,這也是為什么對(duì)于OC來說沒有真正的私有變量,因?yàn)樗鼈兌伎梢允褂肒VC訪問。

二. 使用場景

下面是KVC的一些實(shí)用場景,讀者可自行編碼嘗試。

1.訪問私有屬性

例如設(shè)置UITextField的placeholder顏色,常規(guī)的方法是:

 // 方式一:常規(guī)設(shè)置
 _nameTextField.attributedPlaceholder = [[NSAttributedString alloc] initWithString:@"請(qǐng)輸入名字" attributes:@{NSForegroundColorAttributeName : [UIColor redColor]}];

通過KVC的方式設(shè)置:

// 方式二:使用KVC獲取私有屬性
_nameTextField.placeholder = @"請(qǐng)輸入名字";
// UILabel *placeHolderLabel = [_nameTextField valueForKey:@"placeholderLabel"];
UILabel *placeHolderLabel = [_nameTextField valueForKey:@"_placeholderLabel"];
placeHolderLabel.textColor = UIColor.blackColor;
[self.view addSubview:_nameTextField];

這里通過valueForKey的方式獲取了UITextField的placeholderLabel,然后對(duì)該對(duì)象的顏色進(jìn)行設(shè)置。如果獲取到的placeholderLabel為nil,有幾種可能:

  1. key輸入有誤(重新輸入)
  2. 系統(tǒng)實(shí)現(xiàn)發(fā)生改變
  3. 蘋果使用的Lazy Load,導(dǎo)致在使用valueForKey獲取的時(shí)候還沒有初始化(因此這里先賦值placeholder,然后再獲取placeholderLabel)

還是建議使用使用方式一對(duì)屬性進(jìn)行設(shè)置。站在蘋果的角度,它之所以把某些屬性設(shè)置為私有,就是不想讓開發(fā)者進(jìn)行直接修改,后續(xù)一旦蘋果對(duì)系統(tǒng)實(shí)現(xiàn)有所更改,那就會(huì)導(dǎo)致使用KVC獲取的內(nèi)容失效。
另外,在使用setValue:forKey:的時(shí)候一定要類型統(tǒng)一,比如你通過key獲取到的是一個(gè)Label,卻將string設(shè)置為了value,將會(huì)crash。
上面在使用valueForKey:方法的時(shí)候參數(shù)可以帶下劃線( _ placeholderLabel ),也可以不帶下劃線,它的主要區(qū)別就是如果使用了帶下劃線的key,就算類中手動(dòng)實(shí)現(xiàn)了getter方法,也不會(huì)執(zhí)行類中實(shí)現(xiàn)的getter方法。如果使用了不帶下劃線的,將會(huì)執(zhí)行類中g(shù)etter方法。setter也是如此。

2.對(duì)象關(guān)系映射

在沒有比較成熟的第三方Model解析(如Mantle)前,ORM(Object Relational Mapping)可以使用KVC進(jìn)行處理:

- (instancetype)init {
    return [self initWithJSONDictionary:nil];
}
- (instancetype)initWithJSONDictionary:(NSDictionary *)anDictionary {
    if (self = [super init]) {
        [self setValuesForKeysWithDictionary:anDictionary];
    }
    return self;
}
- (void)setValue:(id)value forUndefinedKey:(NSString *)key {
    NSLog(@"%@",key);
}
//
- (void)objectRelationalMapping {
    NSDictionary *personInfoDictionary = @{
                                           @"name" : @"zhangsan",
                                           @"age" : @"30",
                                           @"school" : @"Hist"
                                           };
    Person *p1 = [[Person alloc] initWithJSONDictionary:personInfoDictionary];
    NSLog(@"%@",p1);
}
@end

這里主要使用了setValuesForKeysWithDictionary:方法。需要注意的是,需要實(shí)現(xiàn)setValue:forUndefinedKey:方法,因?yàn)楫?dāng)字典中包含的key在Person屬性中并不一定存在,如果不存在的話,就會(huì)調(diào)用setValue:forUndefinedKey:。該方法默認(rèn)拋出NSUndefinedKeyException異常。所以需要對(duì)其進(jìn)行重寫,避免Crash。

3. 使用keyPath實(shí)現(xiàn)多級(jí)訪問

KVC除了setValue:forKey:方法,還有setValue:forKeypath:方法。具體使用如下:

_nameTextField.placeholder = @"請(qǐng)輸入名字";
[_nameTextField setValue:UIColor.blackColor forKeyPath:@"placeholderLabel.textColor"];

這里一步操作,就完成了對(duì)placeholder顏色的變更。

4. 安全性訪問

現(xiàn)在Person有一個(gè)friends方法,屬性聲明如下:

@property (nonatomic, copy) NSMutableArray *friends;

此時(shí)可以通過如下的方式進(jìn)行設(shè)置friends:

NSArray *personsArray = ...;
Person *zhangsan = [[Person alloc] init];
zhangsan.name = @"zhangsan";
[[zhangsan mutableArrayValueForKey:@"friends"] addObjectsFromArray:personsArray];

這樣就可以順利將personsArray賦值給zhangsan的friends。接下來換個(gè)操作:將屬性改為

@property (nonatomic, copy) NSArray *friends;

其他代碼保持不變。再次執(zhí)行,依然會(huì)給zhangsan.friends賦值。而且沒有任何crash或者異常。由此可見,通過mutableArrayValueForKey這種方式進(jìn)行處理,可以對(duì)于不可變的集合類型,提供安全的可變?cè)L問,即使是不可變數(shù)組,也可以增加數(shù)組元素。

5. 函數(shù)操作

使用KVC,可以很方便地進(jìn)行一些基本的函數(shù)操作,例如:

NSMutableArray *personsArray = [[NSMutableArray alloc] initWithCapacity:5];
for (NSInteger i = 0; i < 5; i ++) {
    NSString *tempName = [NSString stringWithFormat:@"people%ld",(long)i];
    NSDictionary *personInfoDictionary = @{
                                           @"name" : tempName,
                                           @"age" : @(10 + i),
                                           @"school" : @"Hist"
                                           };
    Person *tempPerson = [[Person alloc] initWithJSONDictionary:personInfoDictionary];
    [personsArray addObject:tempPerson];
}
NSNumber *count = [personsArray valueForKeyPath:@"@count"];
NSNumber *sumAge = [personsArray valueForKeyPath:@"@sum.age"];
NSNumber *avgAge = [personsArray valueForKeyPath:@"@avg.age"];
NSNumber *maxAge = [personsArray valueForKeyPath:@"@max.age"];
NSNumber *minAge = [personsArray valueForKeyPath:@"@min.age"];

其中@表示是數(shù)組特有的鍵,而不是名為count的鍵??梢允褂胿alueForKeyPath:快速進(jìn)行計(jì)算。還可以進(jìn)行更復(fù)雜的一些計(jì)算:

NSArray *array = @[@"apple", @"banner", @"apple", @"orange"];
NSLog(@"%@", [array valueForKeyPath:@"@distinctUnionOfObjects.self"]); // orange,apple,banner

NSArray *array1 = @[@[@"apple", @"banner"], @[@"apple", @"orange"]];
NSLog(@"%@", [array1 valueForKeyPath:@"@unionOfArrays.self"]); // apple,banner,apple,orange

NSMutableArray *personsArray1 = [[NSMutableArray alloc] initWithCapacity:5];
NSMutableArray *personsArray2 = [[NSMutableArray alloc] initWithCapacity:5];
for (NSInteger i = 0; i < 5; i ++) {
    NSString *tempName = [NSString stringWithFormat:@"people%ld",(long)i];
    NSDictionary *personInfoDictionary = @{
                                           @"name" : tempName,
                                           @"age" : @(10 + i),
                                           @"school" : @"Hist"
                                           };
    Person *tempPerson = [[Person alloc] initWithJSONDictionary:personInfoDictionary];
    i % 2 == 0 ? [personsArray1 addObject:tempPerson] : [personsArray2 addObject:tempPerson];
}
NSArray *personsArray = @[personsArray1, personsArray2];
NSLog(@"%@",[[personsArray valueForKeyPath:@"@unionOfArrays.age"] valueForKeyPath:@"@sum.self"]);  // 60

類似上面的代碼,可以使用KVC對(duì)復(fù)雜的操作進(jìn)行簡單化,而沒有必要再使用for循環(huán)或者其他遍歷操作。

三. KVC驗(yàn)證

Person *person = [[Person alloc] init];
[person setValue:[UIColor redColor] forKey:@"name"];

name是string類型,但是傳入一個(gè)UIColor類型也沒有異常或者Crash產(chǎn)生,這也是一個(gè)潛在的問題,一旦按照string使用name,就會(huì)出現(xiàn)問題。因此需要對(duì)value類型與key是否匹配進(jìn)行判斷。KVC提供了如下的方法:

Person *person = [[Person alloc] init];
UIColor *color = [UIColor redColor];
NSError *error = nil;
BOOL isValidate = [person validateValue:&color forKey:@"name" error:&error];
if (isValidate && !error) {
   [person setValue:color forKey:@"name"];
}

發(fā)現(xiàn)依然可以設(shè)置成功,validateValue:forKey:error:方法竟然返回了YES。根據(jù)官方文檔可知,此方法的默認(rèn)實(shí)現(xiàn)將搜索接收方的類,尋找名稱匹配validate:error:模式的驗(yàn)證方法。如果為屬性定義了這樣一個(gè)方法,那么validateValue:forKey:error: 的默認(rèn)實(shí)現(xiàn)在需要被驗(yàn)證的時(shí)候調(diào)用。在為這個(gè)屬性定義的方法中,你可以根據(jù)需要更改輸入的值,或者設(shè)置默認(rèn)值等。如果沒有實(shí)現(xiàn)驗(yàn)證方法,則默認(rèn)返回YES。因此,我們可以在Person類中實(shí)現(xiàn)下面的方法:

- (BOOL)validateName:(id *)ioValue error:(NSError **)error {
    if ([*ioValue isKindOfClass:[NSString class]]) {
        return YES;
    }
    return NO;
}

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    国产精品久久精品国产| 久久精品欧美一区二区三不卡| 国产精品欧美一区二区三区不卡 | 国产一级二级三级观看| 日韩熟妇人妻一区二区三区| 成年女人下边潮喷毛片免费| 亚洲国产成人精品一区刚刚| 激情亚洲一区国产精品久久| 日韩一区二区三区免费av| 亚洲专区一区中文字幕| 好吊妞视频免费在线观看| 国产一区二区精品高清免费 | 色鬼综合久久鬼色88| 日韩精品小视频在线观看| 欧美中文字幕一区在线| 99热中文字幕在线精品| 国产精品免费精品一区二区| 日本理论片午夜在线观看| 日韩中文字幕视频在线高清版| 黄色在线免费高清观看| 激情爱爱一区二区三区| 黄色片国产一区二区三区| 日韩夫妻午夜性生活视频| 欧美一区二区三区播放| 日韩精品视频香蕉视频| 国产一区二区不卡在线视频| 国产又粗又黄又爽又硬的| 成人午夜激情在线免费观看| 日本本亚洲三级在线播放| 色婷婷久久五月中文字幕| 国产真人无遮挡免费视频一区| 91亚洲国产日韩在线| 国产精品一区二区视频| 麻豆蜜桃星空传媒在线观看| 99久久无色码中文字幕免费| 黑丝袜美女老师的小逼逼| 亚洲乱妇熟女爽的高潮片| 精产国品一二三区麻豆| 色婷婷在线视频免费播放| 亚洲精选91福利在线观看| 亚洲国产另类久久精品|