利用 Runtime(運行時)講解字典轉(zhuǎn)模型的核心算法

本人有若干成套學習視頻, 可試看! 可試看! 可試看, 重要的事情說三遍 包含Java, 數(shù)據(jù)結(jié)構(gòu)與算法, iOS, 安卓, python, flutter等等, 如有需要, 聯(lián)系微信tsaievan.

運行時(runtime)是一種面向?qū)ο蟮木幊陶Z言的運行環(huán)境
OC 最主要的特點就是在程序運行時, 以發(fā)送消息的方式調(diào)用方法
運行時時 OC 的核心, OC 就是基于運行時的

上面的話太抽象了, 運行時能干什么? 這才是最關(guān)鍵的, 當我們老是被問到 YYModel 是怎么實現(xiàn)的時候, 一臉懵逼, 其實,在 YYModel 內(nèi)部就運用了運行時, 來字典轉(zhuǎn)模型,具體的思路是這樣的


字典轉(zhuǎn)模型的核心算法思路

以往, 我們字典轉(zhuǎn)模型,總是需要在模型類中定義一個靜態(tài)方法或者對象方法,來字典轉(zhuǎn)模型, 這樣, 我們在不同的模型中, 都必須定義這樣一個方法來完成字典轉(zhuǎn)模型, 如果我們寫的項目比較大, 模型比較多,這樣字典轉(zhuǎn)模型的效率就太低了,耦合性也比較高, 那我們?nèi)绾巫龅阶值滢D(zhuǎn)模型 與 模型類的徹底解耦呢?

我們可以創(chuàng)建一個 NSObject 的分類, 因為所有的類(NSProxy 除外)都繼承自 NSObject, 那我們就可以用任意的類去調(diào) NSObject 的這個分類方法, 子類可以任意調(diào)用父類方法嘛

那么我們?nèi)绾卧谶@個分類方法中完成字典轉(zhuǎn)模型呢?

這里就要用到運行時的概念了,

首先我們在分類中導入 <objc/runtime.h>這個框架, 然后進行第一步,獲取屬性列表
const char *kPropertyListKey = "YFPropertyListKey";

+ (NSArray *)yf_objcProperties
{
     /* 獲取關(guān)聯(lián)對象 */
    NSArray *ptyList = objc_getAssociatedObject(self, kPropertyListKey);
    
     /* 如果 ptyList 有值,直接返回 */
    if (ptyList) {
        return ptyList;
    }
     /* 調(diào)用運行時方法, 取得類的屬性列表 */
    /* 成員變量:
     * class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 方法:
     * class_copyMethodList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 屬性:
     * class_copyPropertyList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 協(xié)議:
     * class_copyProtocolList(__unsafe_unretained Class cls, unsigned int *outCount)
     */
    unsigned int outCount = 0;
    /**
     * 參數(shù)1: 要獲取得類
     * 參數(shù)2: 雷屬性的個數(shù)指針
     * 返回值: 所有屬性的數(shù)組, C 語言中,數(shù)組的名字,就是指向第一個元素的地址
     */
    /* retain, creat, copy 需要release */
    objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
    
    NSMutableArray *mtArray = [NSMutableArray array];
    
     /* 遍歷所有屬性 */
    for (unsigned int i = 0; i < outCount; i++) {
         /* 從數(shù)組中取得屬性 */
        objc_property_t property = propertyList[i];
         /* 從 property 中獲得屬性名稱 */
        const char *propertyName_C = property_getName(property);
         /* 將 C 字符串轉(zhuǎn)化成 OC 字符串 */
        NSString *propertyName_OC = [NSString stringWithCString:propertyName_C encoding:NSUTF8StringEncoding];
        [mtArray addObject:propertyName_OC];
    }
    
     /* 設(shè)置關(guān)聯(lián)對象 */
    /**
     *  參數(shù)1 : 對象self
     *  參數(shù)2 : 動態(tài)添加屬性的 key
     *  參數(shù)3 : 動態(tài)添加屬性值
     *  參數(shù)4 : 對象的引用關(guān)系
     */

    objc_setAssociatedObject(self, kPropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    /* 釋放 */
    free(propertyList);
    return mtArray.copy;
    
}
其實上面這一長串代碼中,只有4句是最關(guān)鍵的

1./* 獲取關(guān)聯(lián)對象 */ NSArray *ptyList = objc_getAssociatedObject(self, kPropertyListKey);
如果在程序運行的時候, 模型對象的屬性是不會發(fā)生變化的, 我們在利用這個函數(shù)如果能獲取到關(guān)聯(lián)對象的屬性列表, 就不用再走下面的代碼去利用運行時再去獲取屬性列表了

2.objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);
這句代碼就是真正的利用運行時獲取屬性列表, 這個屬性列表是 C 的結(jié)構(gòu)體指針數(shù)組,我們必須將其遍歷,并利用另外一個函數(shù)將取出結(jié)構(gòu)體指針所指向的結(jié)構(gòu)體中國的 C 字符串,也就是屬性名稱

3.const char *propertyName_C = property_getName(property);
獲得C字符串后,我們只需要將其轉(zhuǎn)換為 OC 字符串,加到可變數(shù)組中即可

4.objc_setAssociatedObject(self, kPropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
設(shè)置屬性列表, 就是把已經(jīng)生成好的屬性列表設(shè)置到一個類似于屬性的東西儲存起來, 下次 get 的時候,直接拿出來用即可,有點類似于懶加載.

獲取屬性列表之后, 我們就要進行字典轉(zhuǎn)模型的操作了

首先我們要遍歷參數(shù)字典, 如果我們獲取得屬性列表中包含了字典中的 key,就利用 KVC 方法賦值,然后就完成了字典轉(zhuǎn)模型的操作

+ (instancetype)yf_objcWithDict:(NSDictionary *)dict
{
     /* 實例化對象 */
    id objc = [[self alloc]init];
    
     /* 使用字典,設(shè)置對象信息 */
     /* 1. 獲得 self 的屬性列表 */
    NSArray *propertyList = [self  yf_objcProperties];
    
     /* 2. 遍歷字典 */
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
       
         /* 3. 判斷 key 是否字 propertyList 中 */
        if ([propertyList containsObject:key]) {
             /* 說明屬性存在,可以使用 KVC 設(shè)置數(shù)值 */
            [objc setValue:obj forKey:key];
        }
        
    }];
    
     /* 返回對象 */
    return objc;
}

這樣, 比如我在 ViewDidLoad 方法中, 自定義一個字典
然后我只需要一行代碼就可以獲取到模型對象,如下

- (void)viewDidLoad {
    [super viewDidLoad];
    /* 創(chuàng)建一個字典 */
    NSDictionary *dict = @{
                           @"name":@"小明",
                           @"age":@18,
                           @"title":@"master",
                           @"height":@1.7,
                           @"something":@"nothing"
                           };
    
    Person *person = [Person yf_objcWithDict:dict];
}

而此時, 模型類中,沒有添加任何的構(gòu)造方法,只有單純的屬性,這樣就做到了徹底的解耦, 比如我現(xiàn)在再來一個學生(Student)類,我也無需添加構(gòu)造方法,也同樣只需要調(diào)用-(instancetype)yf_objcWithDict:dict;即可.

模型類只有單純的屬性
一行代碼完成字典轉(zhuǎn)模型
這就是一些第三方框架,例如 YYModel,MJExtension等的核心算法,當然,他們會做很多優(yōu)化, 因為實際上的字典轉(zhuǎn)模型是很復雜的, 字典里嵌套數(shù)組,數(shù)組里有數(shù)組,數(shù)組里有字典,字典里又有數(shù)組等等, 這我就 hold不住啦,感謝各位捧場!

PS. 本人有若干成套學習視頻, 包含Java, 數(shù)據(jù)結(jié)構(gòu)與算法, iOS, 安卓, python, flutter等等, 如有需要, 聯(lián)系微信tsaievan.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末凄贩,一起剝皮案震驚了整個濱河市查乒,隨后出現(xiàn)的幾起案子绒障,更是在濱河造成了極大的恐慌涤久,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機玛追,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闲延,“玉大人痊剖,你說我怎么就攤上這事±萘幔” “怎么了陆馁?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長合愈。 經(jīng)常有香客問我叮贩,道長,這世上最難降的妖魔是什么佛析? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任益老,我火速辦了婚禮,結(jié)果婚禮上寸莫,老公的妹妹穿的比我還像新娘捺萌。我一直安慰自己,他們只是感情好膘茎,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布互婿。 她就那樣靜靜地躺著,像睡著了一般辽狈。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上呛牲,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天刮萌,我揣著相機與錄音,去河邊找鬼娘扩。 笑死着茸,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的琐旁。 我是一名探鬼主播涮阔,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼灰殴!你這毒婦竟也來了敬特?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎伟阔,沒想到半個月后辣之,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡皱炉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年怀估,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片合搅。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡多搀,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出灾部,到底是詐尸還是另有隱情康铭,我是刑警寧澤,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布梳猪,位于F島的核電站麻削,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏春弥。R本人自食惡果不足惜呛哟,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望匿沛。 院中可真熱鬧扫责,春花似錦、人聲如沸逃呼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽抡笼。三九已至苏揣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間推姻,已是汗流浹背平匈。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留藏古,地道東北人增炭。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像拧晕,于是被迫代替她去往敵國和親隙姿。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

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