蜜蜂淺談Runtime Part1(菜鳥級(jí)別)

本人是渣渣一枚,最近淺淺的接觸了一下runtime,給大家分享一些我的理解,高手勿噴

runtime簡(jiǎn)稱運(yùn)行時(shí),采用C和匯編寫的,是蘋果為了動(dòng)態(tài)系統(tǒng)的高效而做出的努力. OC從三個(gè)不同的層級(jí)上與runtime進(jìn)行交互,分別為:

1.Object-C源代碼

2.Foundation框架和NSObject類定義的方法

3.Runtime函數(shù)的直接使用

在平時(shí)的開發(fā)中我們只需要寫OC代碼,而runtime會(huì)自動(dòng)在幕后運(yùn)作

1.RunTime簡(jiǎn)稱運(yùn)行時(shí),就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制峭梳,其中最主要的是消息機(jī)制作喘。

2.對(duì)于C語(yǔ)言,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)甘磨,編譯完成之后直接順序執(zhí)行逞泄,無(wú)任何二義性患整。

3.OC的函數(shù)調(diào)用成為消息發(fā)送。屬于動(dòng)態(tài)調(diào)用過(guò)程喷众。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)(事實(shí)證明各谚,在編譯階段,OC可以調(diào)用任何函數(shù)到千,即使這個(gè)函數(shù)并未實(shí)現(xiàn)昌渤,只要申明過(guò)就不會(huì)報(bào)錯(cuò)。而C語(yǔ)言在編譯階段就會(huì)報(bào)錯(cuò))憔四。

只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用膀息。

相信很多人聽了概念,只有個(gè)大概的印象,runtime,其實(shí)最多的運(yùn)用于model,還有交換/增加/跟蹤方法,我寫了幾個(gè)demo助于各位理解

Model實(shí)現(xiàn)NSCoding的自動(dòng)歸檔和解檔
按照我之前比較入門級(jí)的寫法,我們需要對(duì)model的每一個(gè)屬性都實(shí)現(xiàn)一遍encodeObject和decodeObjectForKey方法,那如果有多個(gè)屬性,我們則必須一個(gè)個(gè)的寫一遍,在這里我們就可以采用runtime來(lái)漸變實(shí)現(xiàn)

這里我先看一下runtime的一個(gè)獲取變量列表的方法

#pragma --- 這里先介紹兩個(gè)需要涉及到的runtime的兩個(gè)知識(shí) ---
    // * 獲取屬性列表
    /**
        這里count可以通過(guò) class_copyPropertyList 這個(gè)方法得到屬性的數(shù)量
        [self class] 指的是你想要獲取的那個(gè)類,如果想獲取BeeEncodeModel的屬性則可以改成[BeeEncodeModel class]
        這時(shí)候我們propertylist能夠獲取到類中的所有成員屬性,然后根據(jù)count就可以取到相對(duì)應(yīng)的屬性名稱
    */
    unsigned int count = 0;
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"類中包含屬性:%@", [NSString stringWithUTF8String:propertyName]);
    }
    
    
    // * 獲取成員變量
    /**
        這個(gè)方法跟上面的大同小異,就是方法改為 class_copyPropertyList
        class_copyIvarList:獲取類中的所有成員屬性
        Ivar:成員屬性的意思
        然后這個(gè)方法是可以獲取得到成員變量,我們通過(guò)打印就可以很清楚的看到
    */
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i=0; i<count; i++) {
        Ivar myIvar = ivarList[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"類中包含成員變量%@", [NSString stringWithUTF8String:ivarName]);
    }

這里要注意一下,獲取成員變量的方法因?yàn)槭怯弥羔樦赶蛟氐姆椒?所以得到的變量名字前面會(huì)有一個(gè)"",屬性列表的方法就沒(méi)有"",這里我們可以在控制臺(tái)清楚的看出來(lái)

有了獲取成員變量這個(gè)方法,我們可以動(dòng)態(tài)的控制類的屬性,方法如下:

#pragma mark - 通過(guò)動(dòng)態(tài)變量控制修改BeeEncodeModel的age屬性,把a(bǔ)ge的值改成20
- (void)change_variable{
//    _beeModel = [[BeeEncodeModel alloc]init];
    unsigned int count = 0;
    Ivar *ivar = class_copyIvarList([_beeModel class], &count);
    for (int i = 0; i<count; i++) {
        Ivar var = ivar[i];
        const char  *varName = ivar_getName(var);
        NSString *name = [NSString stringWithUTF8String:varName];
        if ([name isEqualToString:@"_age"]) {
            
// * 在這里也可以把Garlic.h的age為nsnumber類型,則:
            object_setIvar(_beeModel, var, @20);
// * 如果是int等基本數(shù)據(jù)類型,則可以使用強(qiáng)轉(zhuǎn)
//            object_setIvar(beeVC, var, (__bridge id)((void *)20));
            
            break;
        }
    }
    
    NSLog(@"beeVC age is %@",_beeModel.age);
}

接下來(lái)我們創(chuàng)建一個(gè)BeeEncodeModel,我們是用runtime的方法對(duì)這個(gè)model進(jìn)行NSCoding的自動(dòng)歸檔和解檔,同樣的,也是用到了encodeObject和decodeObjectForKey方法

#import <Foundation/Foundation.h>

@interface BeeEncodeModel : NSObject

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *address;
@property (nonatomic, assign) NSNumber *age;

// model 的成員變量不需要全部都存在于字典中
@property (nonatomic, copy) NSString *name1;
@property (nonatomic, copy) NSString *address1;
@property (nonatomic, assign) NSNumber *age1;

@end


#import "BeeEncodeModel.h"

#import <objc/runtime.h>

@implementation BeeEncodeModel

/**
    我們通過(guò)獲取runtime的方法來(lái)獲取BeeEncodeModel的成員屬性,可以得到這個(gè)類所有的成員屬性,然后遍歷一下進(jìn)行歸檔,我們就不用一個(gè)個(gè)去寫了
    這里使用的是獲取的成員變量方法,所以會(huì)有 "_" ,因此我們通過(guò) key = [key substringFromIndex:1] 來(lái)刪除 "_",使得這個(gè)key能跟成員屬性匹配從而進(jìn)行歸檔
    所以我們只需要在.h文件中寫上成員屬性,而.m的代碼是不用動(dòng)的.就已經(jīng)能實(shí)現(xiàn)自動(dòng)歸檔和解檔
*/

- (void)encodeWithCorder
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([BeeEncodeModel class], &count);
    for (int i = 0; i < count; i++) {
        // 取出i對(duì)應(yīng)的成員變量
        Ivar ivar = ivars[i];
        // 查看成員變量
        const char *name = ivar_getName(ivar);
        // 歸檔
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        key = [key substringFromIndex:1];
        // 設(shè)置到成員變量上
        [self setValue:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCorder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([BeeEncodeModel class], &count);
        for (int i = 0; i<count; i++) {
            // 取出i位置對(duì)應(yīng)的成員變量
            Ivar ivar = ivars[i];
            // 查看成員變量
            const char *name = ivar_getName(ivar);
            // 歸檔
            NSString *key = [NSString stringWithUTF8String:name];
            key = [key substringFromIndex:1];
            id value = [decoder decodeObjectForKey:key];
            // 設(shè)置到成員變量身上
            [self setValue:value forKey:key];
        }
        free(ivars);
    }
    return self;
}

@end

這就是運(yùn)用了runtime的方法把屬性都遍歷了出來(lái)然后一個(gè)個(gè)進(jìn)行歸檔和解檔,就不用我們一個(gè)個(gè)去寫了.

當(dāng)然,我覺(jué)得真正強(qiáng)大的并不是在這里,我們可以在model自定義一個(gè)方法

可能我們?cè)L問(wèn)后臺(tái)拿到的json數(shù)據(jù),就是一個(gè)大字典,但是可能大字典里有數(shù)組或者有小字典,這時(shí)我們可以用runtime的方式,自定義一個(gè)方法可以讓這些子字典給弄出來(lái),即一個(gè)字典里面如果包含字典或者數(shù)組,也可以提取里面相對(duì)應(yīng)的key和value直接使用,即讓成員屬性可以跟每個(gè)key相對(duì)應(yīng),話不多說(shuō),上代碼

#import "GarlicModel.h"

#import <objc/runtime.h>

@implementation GarlicModel

/**
    這個(gè)model不需要?dú)w檔和解檔,直接寫一個(gè)類方法
    這個(gè)model可以讓一個(gè)字典里面如果包含字典或者數(shù)組,也可以提取里面相對(duì)應(yīng)的key和value直接使用,即讓成員屬性可以跟每個(gè)key相對(duì)應(yīng)
*/

+ (instancetype)modelWithDict:(NSDictionary *)dict
{
    // 創(chuàng)建對(duì)應(yīng)模型對(duì)象
    id objc = [[self alloc] init];
    
    unsigned int count = 0;
    
    // 1.獲取成員屬性數(shù)組
    Ivar *ivarList = class_copyIvarList(self, &count);
    
    // 2.遍歷所有的成員屬性名,一個(gè)一個(gè)去字典中取出對(duì)應(yīng)的value給模型屬性賦值
    for (int i = 0; i < count; i++) {
        
        // 2.1 獲取成員屬性
        Ivar ivar = ivarList[i];
        
        // 2.2 獲取成員屬性名 C -> OC 字符串
        NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        
        // 2.3 _成員屬性名 => 字典key
        NSString *key = [ivarName substringFromIndex:1];
        
        // 2.4 去字典中取出對(duì)應(yīng)value給模型屬性賦值
        id value = dict[key];
        
        // 獲取成員屬性類型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        
        // 這里要注意判斷value,因?yàn)閙odel的成員屬性不一定存在于字典中,若不存在,value為null
        if (value) {
            NSLog(@"value = %@,key = %@",value,key);
            [objc setValue:value forKey:key];
        }
        
        
        // * 當(dāng)字典里嵌套字典的時(shí)候
        //判斷如果model里有字典
        if ([value isKindOfClass:[NSDictionary class]] && [ivarType containsString:@"NS"]) {
            
            //  是字典對(duì)象,并且屬性名對(duì)應(yīng)類型是自定義類型
            // 處理類型字符串 @\"User\" -> User
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            // 自定義對(duì)象,并且值是字典
            // value:user字典 -> User模型
            // 獲取模型(user)類對(duì)象
            Class modalClass = NSClassFromString(ivarType);
            
            // 字典轉(zhuǎn)模型
            if (modalClass) {
                [objc objectWithDict:value and:objc];
            }
            
        }
        //* 當(dāng)字典里嵌套數(shù)組的時(shí)候
        //判斷如果model有數(shù)組
        if ([value isKindOfClass:[NSArray class]] && [ivarType containsString:@"NS"]){
            
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
            ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
            Class modalClass = NSClassFromString(ivarType);
            if (modalClass) {
                [objc objectWithArray:value and:objc];
            }
        }
        
    }
    return objc;
}

- (void)objectWithDict:(NSDictionary *)dict and:(id)objc
{
    
    unsigned int count = 0;
    
    // 1.獲取成員屬性數(shù)組
    Ivar *ivarList = class_copyIvarList([GarlicModel class], &count);
    
    for (int i = 0; i<count; i++) {
        // 取出i位置對(duì)應(yīng)的成員變量
        Ivar ivar = ivarList[i];
        // 查看成員變量
        const char *name = ivar_getName(ivar);
        // 歸檔
        NSString *key = [NSString stringWithUTF8String:name];
        key = [key substringFromIndex:1];
        
        id value = dict[key];
        
        // 設(shè)置到成員變量身上
        if (value) {
            [objc setValue:value forKey:key];
        }
    }
    
    
}

- (void)objectWithArray:(NSArray *)array and:(id)objc
{
    for (int a = 0; a < array.count; a ++) {
        unsigned int count = 0;
        
        NSDictionary *dict = array[a];
        
        // 1.獲取成員屬性數(shù)組
        Ivar *ivarList = class_copyIvarList([GarlicModel class], &count);
        
        for (int i = 0; i<count; i++) {
            // 取出i位置對(duì)應(yīng)的成員變量
            Ivar ivar = ivarList[i];
            // 查看成員變量
            const char *name = ivar_getName(ivar);
            // 歸檔
            NSString *key = [NSString stringWithUTF8String:name];
            key = [key substringFromIndex:1];
            
            id value = dict[key];
            
            // 設(shè)置到成員變量身上
            if (value) {
                [objc setValue:value forKey:key];
            }
        }
    }
}

@end

這樣子每一個(gè)數(shù)據(jù)里每一個(gè)key都會(huì)和屬性對(duì)應(yīng)起來(lái),無(wú)論是存在字典中的數(shù)組還是字典中的字典.

便于大家理解,我也寫了個(gè)小demo網(wǎng)上
https://github.com/iOSJYF/Garlic_runtime/tree/master/Garlic_WithRuntime
第一次寫文章,大家笑納哈~~

最后編輯于
?著作權(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ì)情侶失蹤饥臂,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(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ó)打工, 沒(méi)想到剛下飛機(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)容

  • 對(duì)于從事 iOS 開發(fā)人員來(lái)說(shuō)靠汁,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,700評(píng)論 7 64
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,686評(píng)論 0 9
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認(rèn)知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 1,935評(píng)論 1 3
  • 閉眼和睜眼之間,恍若已經(jīng)隔了一個(gè)世紀(jì)兄墅,時(shí)間過(guò)得真快踢星,轉(zhuǎn)眼之間一個(gè)月就過(guò)去,來(lái)深圳已經(jīng)一個(gè)多月了察迟,這一個(gè)月里面斩狱,我經(jīng)...
    當(dāng)時(shí)一樣雨閱讀 177評(píng)論 0 0
  • ——來(lái)自官網(wǎng)(澳大利亞駐華大使館) 澳大利亞政府高興地宣布澳大利亞已同中國(guó)達(dá)成協(xié)議對(duì)中國(guó)公民開放打工度假簽證。這種...
    PascalSun閱讀 666評(píng)論 0 1