KVC & KVO

KVC(Key-Value Coding)

KVC最常見(jiàn)的兩個(gè)用途:

  1. 給私有的成員變量賦值(當(dāng)然公有的也可以)

    比如一個(gè)類(lèi)有一個(gè)私有成員變量(在.m文件中)堰怨,用KVC的方式然后可以對(duì)這個(gè)私有成員變量進(jìn)行取值和賦值操作嗜桌。

    Person *person = [Person new];
    [person setValue:@(18) forKey: @"age"];
    

    Person類(lèi)有一個(gè)私有成員變量_age迫摔,我們用KVC的方式可以給其成功賦值。

    也可以用KVC的方式進(jìn)行取值:

    NSNumber *age = [person valueForKey:@"age"];
    

    這里有兩點(diǎn)需要注意:

    • value的值一定是對(duì)象,所以即使person的私有成員變量_age是int類(lèi)型的织鲸,但是setValue的時(shí)候要將其賦值為對(duì)象類(lèi)型的。在進(jìn)行取值操作的時(shí)候溪胶,這里使用NSNumber *類(lèi)型的指針指向這個(gè)對(duì)象搂擦,也可以用NSString *類(lèi)型的指針,因?yàn)関alueForKey:返回的是id類(lèi)型哗脖。
    • 可以看到私有成員變量_age是有下劃線的瀑踢,但是無(wú)論KVC在賦值還是取值的時(shí)候用到的age都是沒(méi)有下劃線的,這樣也可以成功訪問(wèn)這個(gè)值才避。_因?yàn)槭褂肒VC的方式橱夭,會(huì)首先尋找age這個(gè)沒(méi)有下劃線的成員變量,如果查找不到桑逝,會(huì)繼續(xù)查找age這個(gè)有下劃線的成員變量棘劣,所以使用KVC的時(shí)候無(wú)論加不加下劃線都可以。
    • valueForKeyPath:方法更強(qiáng)大楞遏,因?yàn)橛盟梢栽L問(wèn)對(duì)象中的對(duì)象屬性的對(duì)象屬性......就像一個(gè)path一樣可以一直訪問(wèn)下去茬暇。
  2. KVC還有的用途是用于字典轉(zhuǎn)模型

    比如我們有一個(gè)字典

    NSDictionary *dict = @{ @"name": @"Joyann", @"age": @18 };
    

    我們可以直接將字典轉(zhuǎn)換成數(shù)據(jù)模型person:

    Person *person = [Person new];
    NSDictionary *dict = @{ @"name": @"Joyann", @"age": @18 };
    [person setValuesForKeysWithDictionary:dict];
    NSLog(@"name: %@, age: %i", person.name, person.age);
    

    注意,在使用setValuesForKeysWithDictionary:方法進(jìn)行字典轉(zhuǎn)模型的時(shí)候寡喝,要求字典的key和模型類(lèi)的屬性的名字要相同糙俗,并且key的數(shù)量不能多于類(lèi)的屬性,因?yàn)檫@樣就會(huì)造成有的key不能找到相應(yīng)的屬性预鬓。但是是key的數(shù)量是可以少于類(lèi)的屬性的個(gè)數(shù)的巧骚,這樣就是有的類(lèi)屬性不需要賦值(被轉(zhuǎn)換),但是也要保證key和對(duì)應(yīng)的類(lèi)屬性名字要相同格二。

    上面只是簡(jiǎn)單的轉(zhuǎn)換劈彪。

    如果模型類(lèi)有一個(gè)模型屬性,那么需要傳入的字典中的元素也是一個(gè)字典:

    NSDictionary *dict = { @"name": @"joyann", @"age": @(18), @"dog": @{@"name":@"A", @"weiget": @(12.0)} };
    

    在Person類(lèi)中有一個(gè)dog屬性:

    @property (nonatomic, strong) Dog *dog;
    

    Dog類(lèi)中有一個(gè)name屬性蟋定,有一個(gè)weight屬性粉臊。

    此時(shí)想將dict這個(gè)字典轉(zhuǎn)換為數(shù)據(jù)模型則需要下面的操作:

    NSDictionary *dict = { @"name": @"joyann", @"age": @(18), @"dog": @{@"name":@"A", @"weiget": @(12.0)} };
    [person setValusForKeysWithDicatonary: personDict];
    
    person.dog = [[Dog alloc] init]; // 讓指向字典的dog重新指向Dog對(duì)象
    [person.dog setValuesForKeysWithDictonary: personDict[@"dog"]];
    

    當(dāng)給person發(fā)送setValusForKeysWithDicatonary消息的時(shí)候,實(shí)際上此時(shí)它的dog屬性的指針指向了一個(gè)字典驶兜,而不是Dog類(lèi)的對(duì)象扼仲。如果此時(shí)打印person.dog的類(lèi)型,其實(shí)是NSDictionary類(lèi)型抄淑。此時(shí)當(dāng)訪問(wèn)person.dog.name的時(shí)候會(huì)報(bào)錯(cuò)屠凶,因?yàn)閐og不是指的不是一個(gè)對(duì)象,而是一個(gè)字典肆资。

    所以在上面的例子中矗愧,首先要將person.dog重新指向一個(gè)Dog對(duì)象,然后再將字典轉(zhuǎn)換成對(duì)應(yīng)的模型數(shù)據(jù)。

    還有一種情況唉韭,比如一個(gè)模型類(lèi)里面的一個(gè)屬性是NSArray夜涕,這個(gè)數(shù)組里面包含的是其它的對(duì)象屬性。

    比如Person類(lèi)里面:

    @property (nonatomic, strong) NSArray *dogs;
    

    另外還有一個(gè)需要被轉(zhuǎn)換的字典:

    NSDictionary *dict = { @"name": @"joyann", 
                         @"age": @(18),
                            @"dog": @{   @"name": @"A",
                                       @"weiget": @(12.0)}
                           @"dogs": @[ @{@"name": @"B",
                                         "weight": @(13.0)},
                                       @{@"name": @"C",
                                         "weight": @(14.0)}
                                        ]};
    

    此時(shí)情況就很復(fù)雜属愤。我們需要遍歷這個(gè)數(shù)組女器,將其中的字典元素轉(zhuǎn)換成對(duì)應(yīng)的模型數(shù)據(jù)。

    NSDictionary *dict = { @"name": @"joyann", 
                         @"age": @(18),
                            @"dog": @{   @"name": @"A",
                                       @"weiget": @(12.0)}
                           @"dogs": @[ @{@"name": @"B",
                                         "weight": @(13.0)},
                                       @{@"name": @"C",
                                         "weight": @(14.0)}
                                        ]};
    
    [person setValuesForKeysWithDictionary: personDict];
    
    NSMutableArray *tempDogs = [NSMutableArray array];
    for (NSDictionary *dogDict in person.dogs) {
     Dog *dog = [[Dog alloc] init];
         [dog setValuesForKeysWithDictionary: dogDict];
    
         [tempDogs addObject: dog];
    }
    
    person.dogs = tempDogs;
    
  3. 用KVC進(jìn)行取值的補(bǔ)充

    • 前面提到住诸,給一個(gè)對(duì)象發(fā)送valueForKeyPath:也會(huì)提到這個(gè)key對(duì)應(yīng)的value的值驾胆。如果給一個(gè)對(duì)象的數(shù)組屬性發(fā)送這個(gè)消息,那么會(huì)得到這個(gè)數(shù)組中的對(duì)象的value組成的數(shù)組贱呐。

      NSArray *names = [person.dogs valueForKeyPath:@"name"]; // 此時(shí)得到的就是dogs這個(gè)數(shù)組中各個(gè)對(duì)象的name屬性組成的數(shù)組
      
    • 其他應(yīng)用:

      NSLog(@"max:%@", [person.books valueForKeyPath:@"@max.price"]); // 取出價(jià)格數(shù)組中的最大值
      
      NSLog(@"min:%@", [person.books valueForKeyPath:@"@min.price"]); // 取出價(jià)格數(shù)組中的最小值
      
      NSLog(@"avg:%@", [person.books valueForKeyPath:@"@avg.price"]); // 取出價(jià)格數(shù)組的平均值
      
      NSLog(@"count:%@", [person.books valueForKeyPath:@"@count.price"]); // 取出價(jià)格數(shù)組的個(gè)數(shù)
      
      NSLog(@"sum:%@", [person.books valueForKeyPath:@"@sum.price"]); // 取出價(jià)格數(shù)組的總和
      

      ?

    KVO(Key-Value Observing)

    使用KVO可以監(jiān)聽(tīng)對(duì)象的屬性變化丧诺。

    [self.person addObserver:self forKeyPath:@"name" options: NSKeyValueObserveringOptionOld | NSKeyValueObserveringOptionNew context:@"other"];
    

    當(dāng)這行代碼執(zhí)行后,會(huì)讓self來(lái)監(jiān)聽(tīng)self.personname屬性的變化奄薇。當(dāng)name屬性發(fā)生變化時(shí)驳阎,會(huì)給self發(fā)送下面這個(gè)消息:

    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
    

    所以說(shuō)想要監(jiān)聽(tīng)屬性的變化信息,需要實(shí)現(xiàn)上面的回調(diào)方法惕艳。

    keyPath 監(jiān)聽(tīng)的屬性

    object 監(jiān)聽(tīng)的對(duì)象

    change 新值和舊值

    context 額外的數(shù)據(jù)

    當(dāng)給一個(gè)對(duì)象的屬性設(shè)置監(jiān)聽(tīng)者后(在這里是self)搞隐,那么當(dāng)屬性改變的時(shí)候,會(huì)調(diào)用上面的方法远搪,然后將信息通過(guò)參數(shù)傳進(jìn)來(lái)劣纲。如果某個(gè)屬性改變,我們希望做一些額外的操作的話谁鳍,就可以在回調(diào)方法里進(jìn)行癞季。

    注意change這個(gè)參數(shù)是?一個(gè)字典,這里面的值取決于在addObserver:forKeyPath:options:context:中倘潜,options所設(shè)置的值绷柒。

    context參數(shù)是一個(gè)void *類(lèi)型的值,相當(dāng)于任意類(lèi)型涮因,這個(gè)值就是addObserver:forKeyPath:options:context:方法中的context傳過(guò)來(lái)的參數(shù)废睦。

    注意:

    如果進(jìn)行了addObserver操作,那么也要進(jìn)行相應(yīng)的removeObserver操作养泡。

    因?yàn)檫M(jìn)行addObserver操作嗜湃,就會(huì)在通知中心注冊(cè)監(jiān)聽(tīng)者(這里是self)。當(dāng)self這個(gè)對(duì)象銷(xiāo)毀后澜掩,如果沒(méi)有removeObserver购披,那么當(dāng)屬性改變時(shí),仍然會(huì)給self這個(gè)已經(jīng)銷(xiāo)毀的對(duì)象發(fā)送屬性改變的消息肩榕,因?yàn)樵谕ㄖ行闹懈斩福呀?jīng)銷(xiāo)毀的對(duì)象并沒(méi)有撤銷(xiāo)注冊(cè)套啤。

    所以在這個(gè)例子中稽穆,在self銷(xiāo)毀時(shí)應(yīng)該撤銷(xiāo)注冊(cè)監(jiān)聽(tīng)者:

    - (void)dealloc
    {
     [self.person removeObserver:self forKeyPath:@"name"];
    }
    

    ?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市一睁,隨后出現(xiàn)的幾起案子燃逻,更是在濱河造成了極大的恐慌葱色,老刑警劉巖祟霍,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哼凯,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡贮懈,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)优训,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)朵你,“玉大人,你說(shuō)我怎么就攤上這事揣非÷找剑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵早敬,是天一觀的道長(zhǎng)忌傻。 經(jīng)常有香客問(wèn)我,道長(zhǎng)搞监,這世上最難降的妖魔是什么水孩? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮琐驴,結(jié)果婚禮上俘种,老公的妹妹穿的比我還像新娘。我一直安慰自己绝淡,他們只是感情好宙刘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著牢酵,像睡著了一般悬包。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上馍乙,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天布近,我揣著相機(jī)與錄音,去河邊找鬼潘拨。 笑死吊输,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的铁追。 我是一名探鬼主播季蚂,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了扭屁?” 一聲冷哼從身側(cè)響起算谈,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎料滥,沒(méi)想到半個(gè)月后然眼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葵腹,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年高每,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片践宴。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鲸匿,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出阻肩,到底是詐尸還是另有隱情带欢,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布烤惊,位于F島的核電站乔煞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏柒室。R本人自食惡果不足惜渡贾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望伦泥。 院中可真熱鬧剥啤,春花似錦、人聲如沸不脯。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)防楷。三九已至牺丙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間复局,已是汗流浹背冲簿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留亿昏,地道東北人峦剔。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像角钩,于是被迫代替她去往敵國(guó)和親吝沫。 傳聞我的和親對(duì)象是個(gè)殘疾皇子呻澜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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