你會(huì)遇到的runtime面試題(詳)

導(dǎo)讀:
11、12月注定是不太平的月份撕攒,好多小型互聯(lián)網(wǎng)創(chuàng)業(yè)公司都突然崩塌陡鹃,最近一個(gè)朋友跟我抱怨道,說(shuō)終于感受到了互聯(lián)網(wǎng)的瞬息萬(wàn)變了抖坪,昨天我還在公司敲代碼萍鲸,今天就被通知說(shuō)公司倒閉了,可以不用來(lái)公司了擦俐,然后他開始了為期一個(gè)月的找工作經(jīng)歷脊阴,期間問(wèn)到我下面一道題

1、了解runtime嗎蚯瞧?是什么嘿期?
2、你怎么知道的埋合?
3备徐、對(duì)象如何找到對(duì)應(yīng)方法去調(diào)用的

**于是我總結(jié)了很多網(wǎng)上被問(wèn)到的一些關(guān)于runtime的題目,并做了詳細(xì)的回答甚颂,并在后面補(bǔ)充了我在學(xué)習(xí)runtime時(shí)敲的一些代碼蜜猾,如果想吃透runtime的朋友,可以把后面補(bǔ)充的內(nèi)容好好看完 **

一振诬、你會(huì)被問(wèn)到的關(guān)于runtime筆試題:

1. runtime怎么添加屬性蹭睡、方法等
2. runtime 如何實(shí)現(xiàn) weak 屬性
3. runtime如何通過(guò)selector找到對(duì)應(yīng)的IMP地址?(分別考慮類方法和實(shí)例方法)
4. 使用runtime Associate方法關(guān)聯(lián)的對(duì)象赶么,需要在主對(duì)象dealloc的時(shí)候釋放么肩豁?
5. _objc_msgForward函數(shù)是做什么的?直接調(diào)用它將會(huì)發(fā)生什么辫呻?
6. 能否向編譯后得到的類中增加實(shí)例變量清钥?能否向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量?為什么印屁?
7. 簡(jiǎn)述下Objective-C中調(diào)用方法的過(guò)程(runtime)
8. 什么是method swizzling(俗稱黑魔法)

如果上面的題目你全部答得出來(lái)循捺,那就不要浪費(fèi)時(shí)間,直接return吧雄人,程序猿的時(shí)間很寶貴的

二从橘、解答

1. runtime怎么添加屬性、方法等
  • ivar表示成員變量
  • class_addIvar
  • class_addMethod
  • class_addProperty
  • class_addProtocol
  • class_replaceProperty
2. runtime 如何實(shí)現(xiàn) weak 屬性

首先要搞清楚weak屬性的特點(diǎn)
weak策略表明該屬性定義了一種“非擁有關(guān)系” (nonowning relationship)础钠。為這種屬性設(shè)置新值時(shí)恰力,設(shè)置方法既不保留新值,也不釋放舊值旗吁。此特質(zhì)同assign類似;然而在屬性所指的對(duì)象遭到摧毀時(shí)踩萎,屬性值也會(huì)清空(nil out)

那么runtime如何實(shí)現(xiàn)weak變量的自動(dòng)置nil?

runtime對(duì)注冊(cè)的類很钓,會(huì)進(jìn)行布局香府,會(huì)將 weak 對(duì)象放入一個(gè) hash 表中董栽。用 weak 指向的對(duì)象內(nèi)存地址作為 key,當(dāng)此對(duì)象的引用計(jì)數(shù)為0的時(shí)候會(huì)調(diào)用對(duì)象的 dealloc 方法企孩,假設(shè) weak 指向的對(duì)象內(nèi)存地址是a锭碳,那么就會(huì)以a為key,在這個(gè) weak hash表中搜索勿璃,找到所有以a為key的 weak 對(duì)象擒抛,從而設(shè)置為 nil。

weak屬性需要在dealloc中置nil么
在ARC環(huán)境無(wú)論是強(qiáng)指針還是弱指針都無(wú)需在 dealloc 設(shè)置為 nil 补疑, ARC 會(huì)自動(dòng)幫我們處理
即便是編譯器不幫我們做這些歧沪,weak也不需要在dealloc中置nil
在屬性所指的對(duì)象遭到摧毀時(shí),屬性值也會(huì)清空

objc// 模擬下weak的setter方法莲组,大致如下- (void)setObject:(NSObject *)object{ objc_setAssociatedObject(self, "object", object, OBJC_ASSOCIATION_ASSIGN); [object cyl_runAtDealloc:^{ _object = nil; }];}

3. runtime如何通過(guò)selector找到對(duì)應(yīng)的IMP地址诊胞?(分別考慮類方法和實(shí)例方法)
  • 每一個(gè)類對(duì)象中都一個(gè)對(duì)象方法列表(對(duì)象方法緩存)
  • 類方法列表是存放在類對(duì)象中isa指針指向的元類對(duì)象中(類方法緩存)
  • 方法列表中每個(gè)方法結(jié)構(gòu)體中記錄著方法的名稱,方法實(shí)現(xiàn),以及參數(shù)類型,其實(shí)selector本質(zhì)就是方法名稱,通過(guò)這個(gè)方法名稱就可以在方法列表中找到對(duì)應(yīng)的方法實(shí)現(xiàn).
  • 當(dāng)我們發(fā)送一個(gè)消息給一個(gè)NSObject對(duì)象時(shí)锹杈,這條消息會(huì)在對(duì)象的類對(duì)象方法列表里查找
  • 當(dāng)我們發(fā)送一個(gè)消息給一個(gè)類時(shí)厢钧,這條消息會(huì)在類的Meta Class對(duì)象的方法列表里查找
4. 使用runtime Associate方法關(guān)聯(lián)的對(duì)象,需要在主對(duì)象dealloc的時(shí)候釋放么嬉橙?

無(wú)論在MRC下還是ARC下均不需要被關(guān)聯(lián)的對(duì)象在生命周期內(nèi)要比對(duì)象本身釋放的晚很多,它們會(huì)在被 NSObject -dealloc 調(diào)用的object_dispose()方法中釋放
補(bǔ)充:對(duì)象的內(nèi)存銷毀時(shí)間表寥假,分四個(gè)步驟

>1市框、調(diào)用 -release :引用計(jì)數(shù)變?yōu)榱?* 對(duì)象正在被銷毀,生命周期即將結(jié)束. 
* 不能再有新的 __weak 弱引用糕韧,否則將指向 nil.
* 調(diào)用 [self dealloc]
>
2枫振、 父類調(diào)用 -dealloc 
* 繼承關(guān)系中最直接繼承的父類再調(diào)用 -dealloc 
* 如果是 MRC 代碼 則會(huì)手動(dòng)釋放實(shí)例變量們(iVars)
* 繼承關(guān)系中每一層的父類 都再調(diào)用 -dealloc

>3、NSObject 調(diào) -dealloc 
* 只做一件事:調(diào)用 Objective-C runtime 中object_dispose() 方法

>4. 調(diào)用 object_dispose()
* 為 C++ 的實(shí)例變量們(iVars)調(diào)用 destructors
* 為 ARC 狀態(tài)下的 實(shí)例變量們(iVars) 調(diào)用 -release 
* 解除所有使用 runtime Associate方法關(guān)聯(lián)的對(duì)象 
* 解除所有 __weak 引用 
* 調(diào)用 free()
5. _objc_msgForward函數(shù)是做什么的萤彩?直接調(diào)用它將會(huì)發(fā)生什么粪滤?

_objc_msgForward是IMP類型,用于消息轉(zhuǎn)發(fā)的:當(dāng)向一個(gè)對(duì)象發(fā)送一條消息雀扶,但它并沒有實(shí)現(xiàn)的時(shí)候杖小,_objc_msgForward會(huì)嘗試做消息轉(zhuǎn)發(fā)
直接調(diào)用_objc_msgForward是非常危險(xiǎn)
的事,這是把雙刃刀愚墓,如果用不好會(huì)直接導(dǎo)致程序Crash予权,但是如果用得好,能做很多非忱瞬幔酷的事
JSPatch就是直接調(diào)用_objc_msgForward來(lái)實(shí)現(xiàn)其核心功能的
詳細(xì)解說(shuō)參見這里的第一個(gè)問(wèn)題解答

6. 能否向編譯后得到的類中增加實(shí)例變量扫腺?能否向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量?為什么村象?
  • 不能向編譯后得到的類中增加實(shí)例變量笆环;
  • 能向運(yùn)行時(shí)創(chuàng)建的類中添加實(shí)例變量攒至;
  • 分析如下:
    • 因?yàn)榫幾g后的類已經(jīng)注冊(cè)在runtime中,類結(jié)構(gòu)體中的objc_ivar_list 實(shí)例變量的鏈表和instance_size實(shí)例變量的內(nèi)存大小已經(jīng)確定躁劣,同時(shí)runtime 會(huì)調(diào)用class_setIvarLayout 或 class_setWeakIvarLayout來(lái)處理strong weak引用迫吐,所以不能向存在的類中添加實(shí)例變量
    • 運(yùn)行時(shí)創(chuàng)建的類是可以添加實(shí)例變量,調(diào)用 class_addIvar函數(shù)习绢,但是得在調(diào)用objc_allocateClassPair之后渠抹,objc_registerClassPair之前,原因同上闪萄。
7. 簡(jiǎn)述下Objective-C中調(diào)用方法的過(guò)程(runtime)
  • Objective-C是動(dòng)態(tài)語(yǔ)言梧却,每個(gè)方法在運(yùn)行時(shí)會(huì)被動(dòng)態(tài)轉(zhuǎn)為消息發(fā)送,即:objc_msgSend(receiver, selector)败去,整個(gè)過(guò)程介紹如下:
* objc在向一個(gè)對(duì)象發(fā)送消息時(shí)放航,runtime庫(kù)會(huì)根據(jù)對(duì)象的isa指針找到該對(duì)象實(shí)際所屬的類
* 然后在該類中的方法列表以及其父類方法列表中尋找方法運(yùn)行
* 如果,在最頂層的父類(一般也就NSObject)中依然找不到相應(yīng)的方法時(shí)圆裕,程序在運(yùn)行時(shí)會(huì)掛掉并拋出異常unrecognized selector sent to XXX
* 但是在這之前广鳍,objc的運(yùn)行時(shí)會(huì)給出三次拯救程序崩潰的機(jī)會(huì),這三次拯救程序奔潰的說(shuō)明見問(wèn)題《什么時(shí)候會(huì)報(bào)unrecognized selector的異诚抛保》中的說(shuō)明
  • 補(bǔ)充說(shuō)明:Runtime 鑄就了Objective-C 是動(dòng)態(tài)語(yǔ)言的特性赊时,使得C語(yǔ)言具備了面向?qū)ο蟮奶匦裕诔绦蜻\(yùn)行期創(chuàng)建行拢,檢查祖秒,修改類、對(duì)象及其對(duì)應(yīng)的方法舟奠,這些操作都可以使用runtime中的對(duì)應(yīng)方法實(shí)現(xiàn)竭缝。
8. 什么是method swizzling(俗稱黑魔法)
  • 簡(jiǎn)單說(shuō)就是進(jìn)行方法交換
  • 在Objective-C中調(diào)用一個(gè)方法,其實(shí)是向一個(gè)對(duì)象發(fā)送消息沼瘫,查找消息的唯一依據(jù)是selector的名字抬纸。利用Objective-C的動(dòng)態(tài)特性,可以實(shí)現(xiàn)在運(yùn)行時(shí)偷換selector對(duì)應(yīng)的方法實(shí)現(xiàn)耿戚,達(dá)到給方法掛鉤的目的
  • 每個(gè)類都有一個(gè)方法列表湿故,存放著方法的名字和方法實(shí)現(xiàn)的映射關(guān)系,selector的本質(zhì)其實(shí)就是方法名溅话,IMP有點(diǎn)類似函數(shù)指針晓锻,指向具體的Method實(shí)現(xiàn),通過(guò)selector就可以找到對(duì)應(yīng)的IMP
Snip20161207_7.png
  • 交換方法的幾種實(shí)現(xiàn)方式
    • 利用 method_exchangeImplementations 交換兩個(gè)方法的實(shí)現(xiàn)
    • 利用 class_replaceMethod 替換方法的實(shí)現(xiàn)
    • 利用 method_setImplementation 來(lái)直接設(shè)置某個(gè)方法的IMP
      Snip20161207_8.png

三飞几、補(bǔ)充(重要)

1砚哆、消息機(jī)制

  • 1、方法調(diào)用底層實(shí)現(xiàn)


    Snip20161107_5.png
  • 2、runtime:千萬(wàn)不要隨便使用,不得已才使用

//消息機(jī)制:
//作用:調(diào)用已知私有方法躁锁,如調(diào)用沒有在.h文件申明但是在.m文件實(shí)現(xiàn)了的方法
// 用runtime調(diào)用私有方法:方法編號(hào)后面開始,依次就是傳入給方法的參數(shù)

objc_msgSend(p, @selector(run: str:),20,@"haha");
objc_msgSend(p, @selector(eat));
// [p eat] => objc_msgSend(p, @selector(eat));
  • 3纷铣、對(duì)象如何找到對(duì)應(yīng)的方法去調(diào)用

    // 方法保存到什么地方?對(duì)象方法保存到類中,類方法保存到元類(meta class),每一個(gè)類都有方法列表methodList
    //明確去哪個(gè)類中調(diào)用战转,通過(guò)isa指針
    * 1.根據(jù)對(duì)象的isa去對(duì)應(yīng)的類查找方法,isa:判斷去哪個(gè)類查找對(duì)應(yīng)的方法 指向方法調(diào)用的類
    * 2.根據(jù)傳入的方法編號(hào)SEL搜立,里面有個(gè)哈希列表,在列表中找到對(duì)應(yīng)方法Method(方法名)
    * 3.根據(jù)方法名(函數(shù)入口)找到函數(shù)實(shí)現(xiàn)槐秧,函數(shù)實(shí)現(xiàn)在方法區(qū)

2啄踊、交換方法

  • 1、需求:比如我有個(gè)項(xiàng)目,已經(jīng)開發(fā)2年,之前都是使用UIImage去加載圖片,組長(zhǎng)想要在調(diào)用imageNamed,就給我提示,是否加載成功刁标,如果用方法2颠通,每個(gè)調(diào)用imageNamed方法的,都要改成xmg_imageNamed:才能擁有這個(gè)功能膀懈,很麻煩顿锰。解決:用runtime交換方法,即下面方法3
   ①解決方式 自定義UIImage類启搂,缺點(diǎn):每次用要導(dǎo)入自己的類
   ②解決方法:UIImage分類擴(kuò)充一個(gè)這樣方法硼控,缺點(diǎn):需要導(dǎo)入,無(wú)法寫super和self胳赌,會(huì)干掉系統(tǒng)方法牢撼,解決:給系統(tǒng)方法加個(gè)前綴,與系統(tǒng)方法區(qū)分疑苫,如:xmg_imageNamed:
   ③交互方法實(shí)現(xiàn)浪默,步驟: 1.提供分類 2.寫一個(gè)有這樣功能方法 3.用系統(tǒng)方法與這個(gè)功能方法交互實(shí)現(xiàn),在+load方法中實(shí)現(xiàn)
注意:在分類一定不要重寫系統(tǒng)方法,就直接把系統(tǒng)方法干掉缀匕,如果真的想重寫,在系統(tǒng)方法前面加前綴碰逸,方法里面去調(diào)用系統(tǒng)方法

思想:什么時(shí)候需要自定義,系統(tǒng)功能不完善,就自定義一個(gè)這樣類,去擴(kuò)展這個(gè)類
/#import "UIImage+Image.h"
/#import <objc/message.h>
@implementation UIImage (Image)
// 加載類的時(shí)候調(diào)用,肯定只會(huì)調(diào)用一次

 +(void)load
{
    // 交互方法實(shí)現(xiàn)xmg_imageNamed,imageNamed
    /**
     獲取類方法名
     @param Class cls,#> 獲取哪個(gè)類方法 description#>
     @param SEL name#> 方法編號(hào) description#>
     @return 返回Method(方法名)
     class_getClassMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)
     */
    /**
     獲取對(duì)象方法名
     @param Class cls,#> 獲取哪個(gè)對(duì)象方法 description#>
     @param SEL name#> 方法編號(hào) description#>
     @return 返回Method(方法名)
     class_getInstanceMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>)
     */
    
   Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
    Method xmg_imageNameMethod = class_getClassMethod(self, @selector(xmg_imageNamed:));
    //用runtime對(duì)imageNameMethod和xmg_imageNameMethod方法進(jìn)行交換
    method_exchangeImplementations(imageNameMethod, xmg_imageNameMethod);
}
//外界調(diào)用imageNamed:方法乡小,其實(shí)是調(diào)用下面方法,調(diào)用xmg_imageNamed就是調(diào)用imageNamed:
+ (UIImage *)xmg_imageNamed:(NSString *)name
{
    //已經(jīng)把xmg_imageNamed換成imageNamed饵史,所以下面其實(shí)是調(diào)用的imageNamed:
   UIImage *image = [UIImage xmg_imageNamed:name];
    
    if (image == nil) {
        NSLog(@"加載失敗");
    }
    return image;
}
@end

3满钟、動(dòng)態(tài)添加方法

動(dòng)態(tài)添加方法:

為什么動(dòng)態(tài)添加方法? OC都是懶加載,有些方法可能很久不會(huì)調(diào)用
應(yīng)用場(chǎng)景:電商,視頻,社交,收費(fèi)項(xiàng)目:會(huì)員機(jī)制中,只要會(huì)員才擁有這些功能

  • 1胳喷、美團(tuán)面試題:有沒有使用過(guò)performSelector,使用,什么時(shí)候使用?動(dòng)態(tài)添加方法的時(shí)候使用? 為什么動(dòng)態(tài)添加方法
// 默認(rèn)OC方法都有兩個(gè)默認(rèn)存在的隱式參數(shù),self(哪個(gè)類的方法),_cmd(方法編號(hào))
void run(id self, SEL _cmd, NSNumber *metre) {
    NSLog(@"跑了%@",metre);
}
  • 2湃番、什么時(shí)候調(diào)用:只要調(diào)用沒有實(shí)現(xiàn)的方法 就會(huì)調(diào)用方法去解決,這里可以拿到那個(gè)未實(shí)現(xiàn)的方法名
// 作用:去解決沒有實(shí)現(xiàn)方法,動(dòng)態(tài)添加方法
+(BOOL)resolveInstanceMethod:(SEL)sel{
        class:給誰(shuí)添加方法
        SEL:添加哪個(gè)方法
        IMP:方法實(shí)現(xiàn),函數(shù)入口,函數(shù)名吭露,如:(IMP)run吠撮,方法名run強(qiáng)轉(zhuǎn)成IMP
        type:方法類型,通過(guò)查蘋果官方文檔讲竿,V:void泥兰,
     class_addMethod(<#__unsafe_unretained Class cls#>, <#SEL name#>, <#IMP imp#>, <#const char *types#>)
    // [NSStringFromSelector(sel) isEqualToString:@"eat"];
    if (sel == @selector(run:)) {
        // 添加方法
        class_addMethod(self, sel, (IMP)run,"v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

4弄屡、動(dòng)態(tài)添加屬性

  • 1、需求:給NSObject添加一個(gè)name屬性,動(dòng)態(tài)添加屬性 -> runtime

思考:
①給NSObject添加分類鞋诗,在分類中添加屬性膀捷。問(wèn)題:@property在分類中作用:僅僅是生成get,set方法聲明,不會(huì)生成get,set方法實(shí)現(xiàn)和下劃線成員屬性削彬,所以要在.m文件實(shí)現(xiàn)setter/getter方法全庸,用static保存下滑線屬性,這樣一來(lái)融痛,當(dāng)對(duì)象銷毀時(shí)壶笼,屬性無(wú)法銷毀
②用runtime動(dòng)態(tài)添加屬性:本質(zhì)是讓屬性與某個(gè)對(duì)象產(chǎn)生一段關(guān)聯(lián)
使用場(chǎng)景:給系統(tǒng)的類添加屬性時(shí)

#import <objc/message.h>
@implementation NSObject (Property)
//static  NSString *_name;      //通過(guò)這樣去保存屬性沒法做到對(duì)象銷毀,屬性也銷毀酌心,static依然會(huì)讓屬性存在緩存池中拌消,所以需要?jiǎng)討B(tài)的添加成員屬性
// 只要想調(diào)用runtime方法,思考:誰(shuí)的事情
-(void)setName:(NSString *)name
{
    // 保存name
    // 動(dòng)態(tài)添加屬性 = 本質(zhì):讓對(duì)象的某個(gè)屬性與值產(chǎn)生關(guān)聯(lián)
    /*
        object:保存到哪個(gè)對(duì)象中 
        key:用什么屬性保存 屬性名
        value:保存值
        policy:策略,strong,weak
     objc_setAssociatedObject(<#id object#>, <#const void *key#>, <#id value#>, <#objc_AssociationPolicy policy#>)
     */
    objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
//    _name = name;

}

- (NSString *)name
{
    return objc_getAssociatedObject(self, "name");
//    return _name;

}
@end

5、自動(dòng)生成屬性代碼

開發(fā)中安券,從網(wǎng)絡(luò)數(shù)據(jù)中解析出字典數(shù)組墩崩,將數(shù)組轉(zhuǎn)為模型時(shí),如果有幾百個(gè)key需要用侯勉,要寫很多@property成員屬性鹦筹,下面提供一個(gè)萬(wàn)能的方法,可直接將字典數(shù)組轉(zhuǎn)為全部@property成員屬性址貌,打印出來(lái)铐拐,這樣直接復(fù)制在模型中就好了

#import "NSDictionary+PropertyCode.h"
@implementation NSDictionary (PropertyCode)

//1??通過(guò)這個(gè)方法,自動(dòng)將字典轉(zhuǎn)成模型中需要用的屬性代碼

// 私有API:真實(shí)存在,但是蘋果沒有暴露出來(lái),不給你练对。如BOOL值遍蟋,不知道類型,打印得知是__NSCFBoolean螟凭,但是無(wú)法敲出來(lái)虚青,只能用NSClassFromString(@"__NSCFBoolean")

// isKindOfClass:判斷下是否是當(dāng)前類或者子類,BOOL是NSNumber的子類螺男,要先判斷BOOL
- (void)createPropetyCode
{
    // 模型中屬性根據(jù)字典的key
    // 有多少個(gè)key,生成多少個(gè)屬性
    NSMutableString *codes = [NSMutableString string];
    // 遍歷字典
    [self enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull value, BOOL * _Nonnull stop) {
        NSString *code = nil;
        

//        NSLog(@"%@",[value class]);
        
        if ([value isKindOfClass:[NSString class]]) {
          code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSString *%@;",key];
        } else if ([value isKindOfClass:NSClassFromString(@"__NSCFBoolean")]){
            code = [NSString stringWithFormat:@"@property (nonatomic, assign) BOOL %@;",key];
        } else if ([value isKindOfClass:[NSNumber class]]) {
             code = [NSString stringWithFormat:@"@property (nonatomic, assign) NSInteger %@;",key];
        } else if ([value isKindOfClass:[NSArray class]]) {
            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSArray *%@;",key];
        } else if ([value isKindOfClass:[NSDictionary class]]) {
            code = [NSString stringWithFormat:@"@property (nonatomic, strong) NSDictionary *%@;",key];
        }
        
        // 拼接字符串
        [codes appendFormat:@"%@\n",code];

    }];
    
    NSLog(@"%@",codes);
    
}

@end

6棒厘、KVC字典轉(zhuǎn)模型

// 需求:就是在開發(fā)中,通常后臺(tái)會(huì)給你很多數(shù)據(jù),但是并不是每個(gè)數(shù)據(jù)都有用,這些沒有用的數(shù)據(jù),需不需要保存到模型中

@implementation Status
+ (instancetype)statusWithDict:(NSDictionary *)dict{
    // 創(chuàng)建模型
    Status *s = [[self alloc] init];
    
    // 字典value轉(zhuǎn)模型屬性保存
    [s setValuesForKeysWithDictionary:dict];

//    s.reposts_count = dict[@"reposts_count"];
    // 4??MJExtension:可以字典轉(zhuǎn)模型,而且可以不用與字典中屬性一一對(duì)應(yīng),runtime,遍歷模型中有多少個(gè)屬性,直接去字典中取出對(duì)應(yīng)value,給模型賦值
    
    // 1??setValuesForKeysWithDictionary:方法底層實(shí)現(xiàn):遍歷字典中所有key,去模型中查找對(duì)應(yīng)的屬性,把值給模型屬性賦值,即調(diào)用下面方法:
    /*
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        // source
        // 這行代碼才是真正給模型的屬性賦值
        [s setValue:dict[@"source"] forKey:@"source"];      //底層實(shí)現(xiàn)是:
                                 2?? [s setValue:dict[@"source"] forKey:@"source"];
                                 1.首先會(huì)去模型中查找有沒有setSource方法,直接調(diào)用set方法 [s setSource:dict[@"source"]];
                                 2.去模型中查找有沒有source屬性,source = dict[@"source"]
                                 3.去米線中查找有沒有_source屬性,_source = dict[@"source"]
                                 4.調(diào)用對(duì)象的 setValue:forUndefinedKey:直接報(bào)錯(cuò)
        [s setValue:obj forKey:key];
    }];
    */
    return s;
}
     

// 3??用KVC下隧,不想讓系統(tǒng)報(bào)錯(cuò)奢人,重寫系統(tǒng)方法思想:
// 1.想給系統(tǒng)方法添加功能
// 2.不想要系統(tǒng)實(shí)現(xiàn)
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
}


@end

7、MJExtention的底層實(shí)現(xiàn)

#import "NSObject+Model.h"
#import <objc/message.h>

//    class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>) 獲取屬性列表

@implementation NSObject (Model)

/**
 字典轉(zhuǎn)模型
 @param dict 傳入需要轉(zhuǎn)模型的字典
 @return 賦值好的模型
 */

+ (instancetype)modelWithDict:(NSDictionary *)dict

{
    id objc = [[self alloc] init];

    //思路: runtime遍歷模型中屬性,去字典中取出對(duì)應(yīng)value,在給模型中屬性賦值
    // 1.獲取模型中所有屬性 -> 保存到類
    // ivar:下劃線成員變量 和 Property:屬性
    // 獲取成員變量列表
    // class:獲取哪個(gè)類成員變量列表
    // count:成員變量總數(shù)
    //這個(gè)方法得到一個(gè)裝有成員變量的數(shù)組
    //class_copyIvarList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)
    
    int count = 0;
    // 成員變量數(shù)組 指向數(shù)組第0個(gè)元素
    Ivar *ivarList = class_copyIvarList(self, &count);


    // 遍歷所有成員變量
    for (int i = 0; i < count; i++) {
        
        // 獲取成員變量 user
        Ivar ivar = ivarList[i];
        // 獲取成員變量名稱淆院,即將C語(yǔ)言的字符轉(zhuǎn)為OC字符串
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        // 獲取成員變量類型何乎,用于獲取二級(jí)字典的模型名字
        NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        
        //  將type這樣的字符串@"@\"User\"" 轉(zhuǎn)成 @"User"
        type = [type stringByReplacingOccurrencesOfString:@"@\"" withString:@""];
        type = [type stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        
        // 成員變量名稱轉(zhuǎn)換key,即去掉成員變量前面的下劃線
        NSString *key = [ivarName substringFromIndex:1];
        
        // 從字典中取出對(duì)應(yīng)value dict[@"user"] -> 字典
        id value = dict[key];
        
        // 二級(jí)轉(zhuǎn)換
        // 并且是自定義類型,才需要轉(zhuǎn)換
        if ([value isKindOfClass:[NSDictionary class]] && ![type containsString:@"NS"]) { // 只有是字典才需要轉(zhuǎn)換
           
            Class className = NSClassFromString(type);
            
            // 字典轉(zhuǎn)模型
            value = [className modelWithDict:value];
        }
        
        // 給模型中屬性賦值 key:user value:字典 -> 模型
        if (value) {
            [objc setValue:value forKey:key];
        }
        
    }
    
    return objc;

}

@end

更多內(nèi)容請(qǐng)持續(xù)關(guān)注我的簡(jiǎn)書和主頁(yè)博客……

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市宪赶,隨后出現(xiàn)的幾起案子宗弯,更是在濱河造成了極大的恐慌,老刑警劉巖搂妻,帶你破解...
    沈念sama閱讀 210,978評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蒙保,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡欲主,警方通過(guò)查閱死者的電腦和手機(jī)邓厕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)扁瓢,“玉大人详恼,你說(shuō)我怎么就攤上這事∫福” “怎么了昧互?”我有些...
    開封第一講書人閱讀 156,623評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)伟桅。 經(jīng)常有香客問(wèn)我敞掘,道長(zhǎng),這世上最難降的妖魔是什么楣铁? 我笑而不...
    開封第一講書人閱讀 56,324評(píng)論 1 282
  • 正文 為了忘掉前任玖雁,我火速辦了婚禮,結(jié)果婚禮上盖腕,老公的妹妹穿的比我還像新娘赫冬。我一直安慰自己,他們只是感情好溃列,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評(píng)論 5 384
  • 文/花漫 我一把揭開白布劲厌。 她就那樣靜靜地躺著,像睡著了一般听隐。 火紅的嫁衣襯著肌膚如雪脊僚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,741評(píng)論 1 289
  • 那天遵绰,我揣著相機(jī)與錄音,去河邊找鬼增淹。 笑死椿访,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的虑润。 我是一名探鬼主播成玫,決...
    沈念sama閱讀 38,892評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了哭当?” 一聲冷哼從身側(cè)響起猪腕,我...
    開封第一講書人閱讀 37,655評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎钦勘,沒想到半個(gè)月后陋葡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,104評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡彻采,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年腐缤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片肛响。...
    茶點(diǎn)故事閱讀 38,569評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岭粤,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出特笋,到底是詐尸還是另有隱情剃浇,我是刑警寧澤,帶...
    沈念sama閱讀 34,254評(píng)論 4 328
  • 正文 年R本政府宣布猎物,位于F島的核電站虎囚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏霸奕。R本人自食惡果不足惜溜宽,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望质帅。 院中可真熱鬧适揉,春花似錦、人聲如沸煤惩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)魄揉。三九已至剪侮,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洛退,已是汗流浹背瓣俯。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兵怯,地道東北人彩匕。 一個(gè)月前我還...
    沈念sama閱讀 46,260評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像媒区,于是被迫代替她去往敵國(guó)和親驼仪。 傳聞我的和親對(duì)象是個(gè)殘疾皇子掸犬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評(píng)論 2 348

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評(píng)論 0 9
  • 對(duì)于從事 iOS 開發(fā)人員來(lái)說(shuō)绪爸,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,700評(píng)論 7 64
  • objc_getAssociatedObject返回與給定鍵的特定對(duì)象關(guān)聯(lián)的值湾碎。ID objc_getAssoci...
    有一種再見叫青春閱讀 1,567評(píng)論 0 7
  • 之前學(xué)習(xí)前端中,對(duì)meta標(biāo)簽的了解僅僅只是這一句奠货。 但是打開任意的網(wǎng)站介褥,其head標(biāo)簽內(nèi)都有一列的meta標(biāo)簽。...
    璐璐熙可閱讀 294評(píng)論 1 2
  • 一一一在周水子街道關(guān)工委《做營(yíng)商環(huán)境小小維護(hù)者》主題活動(dòng)啟動(dòng)儀式上的講稿 同學(xué)們:大家好仇味! 今天呻顽,我們周水子街道...
    楚國(guó)人閱讀 647評(píng)論 0 0