kvc

一、定義

KVC(Key-value coding)鍵值編碼,對NSObjcet的擴(kuò)展土涝,開發(fā)者通過Key名直接訪問對象的屬性,或者給對象的屬性賦值濒憋。而不需要調(diào)用明確的存取方法何暇。

二、主要使用場景

1.動(dòng)態(tài)地設(shè)值和取值
可以在運(yùn)行時(shí)動(dòng)態(tài)地訪問和修改對象的屬性凛驮;例如進(jìn)行json轉(zhuǎn)model操作時(shí)裆站,可以使用runtime來獲取成員變量,并利用KVC進(jìn)行修改黔夭。

2.訪問和修改私有變量
當(dāng)我們需要訪問和修改.m文件中聲明私有屬性宏胯,KVC是一大利器;例如更改某些三方庫或者系統(tǒng)的私有變量本姥。

三肩袍、調(diào)用順序

對于平時(shí)開發(fā)來說,調(diào)用順序我們并不需要太多了解婚惫,為了更好的理解實(shí)現(xiàn)原理氛赐,這里進(jìn)行簡單梳理

新建Person

 @interface Person : NSObject
{
 NSString *_name;
 NSString *_isName;
 NSString *name;
 NSString *isName;
}
@end

@implementation Person
-(instancetype)init {
 if (self = [super init]) {
     _name = @"這是_name";
     _isName = @"這是_isName";
     name = @"這是name";
     isName = @"這是isName";
 }
 return self;
}
@end

- (void)viewDidLoad {
 [super viewDidLoad];
 
 Person *person = [[Person alloc] init];
 NSString *name = [person valueForKey:@"name"];
 NSLog(@"%@",name);
 
}

打印結(jié)果

2020-04-13 17:12:27.622399+0800 Algorithm[18089:316164] 這是_name
1

這個(gè)時(shí)候假設(shè)沒有"_name"屬性

@interface Person : NSObject
{
//    NSString *_name;
    NSString *_isName;
    NSString *name;
    NSString *isName;
}
@end

@implementation Person
-(instancetype)init {
    if (self = [super init]) {
//        _name = @"這是_name";
        _isName = @"這是_isName";
        name = @"這是name";
        isName = @"這是isName";
    }
    return self;
}
@end

打印結(jié)果

2020-04-13 17:19:57.662839+0800 Algorithm[18327:321939] 這是_isName

由此依次操作,可查找一個(gè)命名規(guī)則為_name先舷、_isName艰管、name、isName的實(shí)例變量密浑。根據(jù)這個(gè)順序蛙婴,如果發(fā)現(xiàn)則獲取實(shí)例變量值。

你以為這樣就結(jié)束了尔破,不可能的街图,這個(gè)時(shí)候來一個(gè)騷操作

// Person.m
// 是否允許讀取實(shí)例變量的值,如果為YES則在KVC查找的過程中懒构,從內(nèi)存中讀取屬性餐济、實(shí)例變量的值。
// 如果不允許外界通過KVC對我們的私有屬性和成員變量進(jìn)行操作胆剧,則可以設(shè)置此值為NO絮姆。
+ (BOOL)accessInstanceVariablesDirectly {
    return NO;
}

這個(gè)時(shí)候就崩潰了,可以看出不能進(jìn)行訪問屬性

  2020-04-14 13:35:21.245193+0800 Algorithm[10324:177961] *** Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<Person 0x600003fc9320> valueForUndefinedKey:]: this class is not key value coding-compliant for the key name.'

再來一個(gè)騷操作

// Person.m
-(NSString *)getName {
    return  @"這是方法getName";
}

-(NSString *)name {
    return  @"這是方法name";
}

-(NSString *)isName {
    return  @"這是方法isName";
}

加入上面方法后又正常了秩霍,完整的調(diào)用順序如下:

1篙悯、查找是否實(shí)現(xiàn)getter方法,依次匹配-get<Key> 和 -<key> 和 -is<Key>和-_<key>铃绒,如果找到鸽照,直接返回。
2颠悬、當(dāng)沒有找到getter方法矮燎,調(diào)用accessInstanceVariablesDirectly詢問
如果返回YES定血, _<key> ,_is<Key>,<key>,is<Key>诞外,找到了返回對應(yīng)的值
如果返回NO澜沟,結(jié)束查找。并調(diào)用 valueForUndefinedKey: 報(bào)異常
3峡谊、如果沒找到getter方法和屬性值茫虽,調(diào)用 valueForUndefinedKey: 報(bào)異常

四、實(shí)現(xiàn)原理

結(jié)合demo靖苇,了解實(shí)現(xiàn)原理(感覺蘋果的底層原理大多都是和runtime有關(guān)席噩。。贤壁。這個(gè)也不例外)直接上代碼

// NSObject+MyKVC.h

@interface NSObject (MyKVC)

-(void)my_setValue:(id)value forKey:(NSString*)key;
-(id)my_valueforKey:(NSString*)key;

@end

// NSObject+MyKVC.m
#import "NSObject+MyKVC.h"
#import <objc/runtime.h>

@implementation NSObject (MyKVC)

-(void)my_setValue:(id)value forKey:(NSString *)key {
    if (key == nil || key.length == 0) {
        return;
    }
    if (value == nil) {
        [self setNilValueForKey:key];
        return;
    }
    if (![value isKindOfClass:[NSObject class]]) {
        @throw @"必須是NSObject類型";
        return;
    }
    NSString *setter = [[@"set" stringByAppendingString:[key capitalizedString]] stringByAppendingString:@":"];
    if ([self respondsToSelector:NSSelectorFromString(setter)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        [self performSelector:NSSelectorFromString(setter) withObject:value];
#pragma clang diagnostic pop
    }else{
        // 獲取屬性列表
        unsigned int count;
        BOOL flag = false;
        Ivar* vars = class_copyIvarList([self class], &count);
        for (NSInteger i = 0; i<count; i++) {
            Ivar var = vars[I];
            NSString* keyName = [NSString stringWithCString:ivar_getName(var) encoding:NSUTF8StringEncoding];
            
            // 按順序匹配到就結(jié)束
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) {
                flag = true;
                object_setIvar(self, var, value);
                break;
            }
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",key.capitalizedString]]) {
                flag = true;
                object_setIvar(self, var, value);
                break;
            }
            
            if ([keyName isEqualToString:key]) {
                flag = true;
                object_setIvar(self, var, value);
                break;
            }
            if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",key.capitalizedString]]) {
                flag = true;
                object_setIvar(self, var, value);
                break;
            }
        }
        // 沒有找到
        if (!flag) {
            [self setValue:value forUndefinedKey:key];
        }
    }
    
}

-(id)my_valueforKey:(NSString *)key{
    
    if (key == nil || key.length == 0) {
        return nil;
    }
    //查找getter方法
    NSString* getFuncName = [NSString stringWithFormat:@"get%@",key.capitalizedString];
    NSString* isFuncName = [NSString stringWithFormat:@"is%@",key.capitalizedString];
    
    // 查找 -get<Key>
    if ([self respondsToSelector:NSSelectorFromString(getFuncName)]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        return [self performSelector:NSSelectorFromString(getFuncName)];
#pragma clang diagnostic pop
    } else if ([self respondsToSelector:NSSelectorFromString(key)]) { // 查找 -<key>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        return [self performSelector:NSSelectorFromString(key)];
#pragma clang diagnostic pop
    } else if ([self respondsToSelector:NSSelectorFromString(isFuncName)]) { // 查找 is<Key>
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
        return [self performSelector:NSSelectorFromString(isFuncName)];
#pragma clang diagnostic pop
    } else { // 詢問 accessInstanceVariablesDirectly悼枢,是否繼續(xù)查找屬性,默認(rèn)返回YES
        
        // 先看順序:_<key>, _is<Key>, <key>, is<Key>
        
        // 獲取屬性列表
        unsigned int count;
        BOOL flag = false;
        Ivar* vars = class_copyIvarList([self class], &count);
        for (NSInteger i = 0; i<count; i++) {
            Ivar var = vars[I];
            NSString* keyName = [NSString stringWithCString:ivar_getName(var) encoding:NSUTF8StringEncoding];
            
            // 按順序匹配到就結(jié)束
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_%@",key]]) {
                flag = true;
                return object_getIvar(self, var);
                break;
            }
            if ([keyName isEqualToString:[NSString stringWithFormat:@"_is%@",key.capitalizedString]]) {
                flag = true;
                return object_getIvar(self, var);
                break;
            }
            if ([keyName isEqualToString:key]) {
                flag = true;
                return object_getIvar(self, var);
                break;
            }
            if ([keyName isEqualToString:[NSString stringWithFormat:@"is%@",key.capitalizedString]]) {
                flag = true;
                return object_getIvar(self, var);
                break;
            }
        }
        // 沒有找到
        if (!flag) {
            [self valueForUndefinedKey:key];
        }
        return nil;
        
    }
    return nil;
}
@end

調(diào)用代碼

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *person = [[Person alloc] init];
    NSString *name = [person my_valueforKey:@"name"];
    NSLog(@"%@",name);
}

image.png
image.png

原創(chuàng)出處:https://blog.csdn.net/qq_32644987/article/details/105489083

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末脾拆,一起剝皮案震驚了整個(gè)濱河市馒索,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌名船,老刑警劉巖绰上,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異渠驼,居然都是意外死亡蜈块,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進(jìn)店門迷扇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來百揭,“玉大人,你說我怎么就攤上這事蜓席∑饕唬” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵厨内,是天一觀的道長祈秕。 經(jīng)常有香客問我,道長雏胃,這世上最難降的妖魔是什么请毛? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮瞭亮,結(jié)果婚禮上获印,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好兼丰,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唆缴,像睡著了一般鳍征。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上面徽,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天艳丛,我揣著相機(jī)與錄音,去河邊找鬼趟紊。 笑死氮双,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的霎匈。 我是一名探鬼主播戴差,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼铛嘱!你這毒婦竟也來了暖释?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤墨吓,失蹤者是張志新(化名)和其女友劉穎球匕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體帖烘,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡亮曹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了秘症。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片照卦。...
    茶點(diǎn)故事閱讀 38,064評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖历极,靈堂內(nèi)的尸體忽然破棺而出窄瘟,到底是詐尸還是另有隱情,我是刑警寧澤趟卸,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布蹄葱,位于F島的核電站,受9級特大地震影響锄列,放射性物質(zhì)發(fā)生泄漏图云。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一邻邮、第九天 我趴在偏房一處隱蔽的房頂上張望竣况。 院中可真熱鬧,春花似錦筒严、人聲如沸丹泉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽摹恨。三九已至筋岛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間晒哄,已是汗流浹背睁宰。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留寝凌,地道東北人柒傻。 一個(gè)月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像较木,于是被迫代替她去往敵國和親红符。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容

  • KVC定義 KVC(Key-value coding)鍵值編碼劫映,就是指iOS的開發(fā)中违孝,可以允許開發(fā)者通過Key名直...
    SheIsMySin_72e7閱讀 376評論 0 0
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中泳赋,可以允許開發(fā)者通過K...
    暮年古稀ZC閱讀 2,128評論 2 9
  • KVC KVC定義 KVC(Key-value coding)鍵值編碼雌桑,就是指iOS的開發(fā)中,可以允許開發(fā)者通過K...
    戀空K閱讀 711評論 0 2
  • image.png KVC KVC定義 KVC(Key-value coding)鍵值編碼祖今,就是指iOS的開發(fā)中校坑,...
    草根小強(qiáng)閱讀 277評論 0 0
  • 1. Basic methods KVC(Key-value coding)鍵值編碼,就是指iOS的開發(fā)中千诬,可以允...
    木小易Ying閱讀 188評論 0 4