runtime

runtime 簡介

傳說中的 runtime, 從開始接觸時(shí)就聽說.但是很少真正用到.了解了 runtime, 就了解了 OC 的運(yùn)行時(shí)機(jī)制.
對于 C 語言,函數(shù)調(diào)用是在編譯時(shí)決定的.對于 OC 就不同了. OC 屬于動(dòng)態(tài)調(diào)用過程,在編譯的時(shí)候不能真正決定調(diào)用哪個(gè)函數(shù),只有在運(yùn)行時(shí),才根據(jù)方法名去找到相應(yīng)的方法進(jìn)行調(diào)用.所以在編譯階段,即使函數(shù)(方法)沒有實(shí)現(xiàn),只要聲明過就不會(huì)報(bào)錯(cuò).

消息機(jī)制

只要接觸過 OC 的人,都會(huì)聽過消息機(jī)制. OC中的叫法不同的函數(shù)(方法)調(diào)用,叫做發(fā)消息.這個(gè)消息怎么來的呢?這就是下面說的runtime消息機(jī)制.
下面用簡單的例子看一下(PS: 先使用命令行).
先定義一個(gè)類.

// .h
@interface DZPerson : NSObject

@property (nonatomic, copy) NSString *name;
- (void)eat;
@end

// .m
#import "DZPerson.h"

@implementation DZPerson
- (void)eat {
    NSLog(@"%@吃東西", self.name);
}
@end

在. h 文件中公開了一個(gè)屬性和一個(gè)方法.
在 main 函數(shù)里我們先來看一下簡單的調(diào)用

#import <Foundation/Foundation.h>
#import "DZPerson.h"

int main(int argc, const char * argv[]) {
    DZPerson *p = [[DZPerson alloc] init];
    p.name = @"dz";
    // 這里的點(diǎn)語法相當(dāng)于調(diào)用 set 方法
    //  [p setName:@"dz"];
    NSLog(@"%@", p.name);
    // 這里的點(diǎn)語法相當(dāng)于調(diào)用 get 方法
   // NSLog(@"%@", [p name]);
    return 0;
}

對于 set 和 get 方法應(yīng)該都不陌生吧. 好,那么就上正菜.就來看看調(diào)用方法是怎么回事.
由于現(xiàn)在蘋果不建議改底層方法.所以,首先需要設(shè)置一下環(huán)境.
在 TARGETS -->BuildSettings 修改一個(gè)參數(shù)如下圖所示,改成 No.然后就可以使用 Runtime 的一些方法了.


在使用時(shí)先導(dǎo)入頭文件#import <objc/message.h>
至于如何發(fā)消息的呢?還有一點(diǎn)要說的,下邊調(diào)用的都是 C 函數(shù).比如我們調(diào)用一個(gè) eat 方法.

// 在 OC 調(diào)用 eat 方法
// [p eat];
// 使用消息機(jī)制發(fā)一個(gè)消息,在 OC 的底層 會(huì)直接給我們轉(zhuǎn)化為objc_msgSend函數(shù)
objc_msgSend( p, @selector(eat));

現(xiàn)在是一個(gè)對象在調(diào)用方法,那么一個(gè)類怎么發(fā)消息呢.那就牽扯到我們所說的類對象了.比如在給 DZPerson定義一個(gè) 方法+ (void)height:(NSInteger)cm;

 // 創(chuàng)建一個(gè)類對象
    Class pClass = [DZPerson class];
  // 給類對象發(fā)消息,方法后面是傳入的參數(shù)
    objc_msgSend(pClass, @selector(height:), 180);

雖然知道這么寫,但是有什么用呢?

給分類添加屬性

我們知道,分類一般只能添加方法,不能添加屬性.有時(shí)候?yàn)榱嗽谙到y(tǒng)類的基礎(chǔ)上不得不做些改變.那么就用 Runtime 進(jìn)行改變.所說的添加屬性其實(shí)本質(zhì)上并不是生成成員變量,而是在分類中提供 set和 get 方法供外界進(jìn)行調(diào)用.表面上和屬性使用一致.

// .h
@interface NSObject (Test)
// @property在分類中,只生成get,set方法的聲明矾利,不會(huì)生成下劃線成員變量,get,set方法的實(shí)現(xiàn)
@property (nonatomic, copy) NSString *name;
@end

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

@implementation NSObject (Test)

// 定義關(guān)聯(lián)的 Key 值
static const char *key = "name";

- (void)setName:(NSString *)name {
  /*
  參數(shù)1:  給哪個(gè)對象添加關(guān)聯(lián)值 
  參數(shù)2: 關(guān)聯(lián)的 key, 通過 key 值進(jìn)行獲取
  參數(shù)3: 關(guān)聯(lián) value
  參數(shù)4: 關(guān)聯(lián)的策略模式(相當(dāng)于, assign, copy, strong...)
*/
    objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)name {
    return objc_getAssociatedObject(self, key);
}
@end

交換兩個(gè)方法

// 給 Person 添加兩個(gè)方法
// .h
- (void)firstMethod;
- (void)secondMethod;

// .m
- (void)firstMethod {
    NSLog(@"first");
}

- (void)secondMethod {
    NSLog(@"second");
}

當(dāng)初始化的時(shí)候我們將兩個(gè)方法交換一下

+ (void)initialize
{
    if (self == [DZPerson class]) {
        [self changeMethod];
    }
}

+ (void)changeMethod {
    Method m1 = class_getInstanceMethod(self, @selector(firstMethod));
    Method m2 = class_getInstanceMethod(self, @selector(secondMethod));
    method_exchangeImplementations(m1, m2);
}

這樣子當(dāng)我們在主函數(shù)中調(diào)用Person 的方法時(shí)

// .m
  DZPerson *p = [[DZPerson alloc] init];
// 調(diào)用 first 會(huì)打印second
[p firstMethod];

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

// 定義函數(shù)
// 沒有返回值,有兩個(gè)默認(rèn)參數(shù)(id SEL)void thirdMethod (id self, SEL _cmd, id par) {
    NSLog(@"%@ %@ %@", self, NSStringFromSelector(_cmd), par);
}

// 當(dāng)對象調(diào)用沒有實(shí)現(xiàn)的方法,會(huì)調(diào)用
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(thirdMethod:)) {
       /*
        cls: 給哪個(gè)類添加
        SEL: 方法編號
        IMP: 方法實(shí)現(xiàn),函數(shù)名
        types: 方法類型
        */
        class_addMethod(self, sel, (IMP)thirdMethod, "V@:@");
    }
    return [super resolveInstanceMethod:sel];
}

轉(zhuǎn)模型(或修改系統(tǒng)控件)

對于某些控件來說,由于蘋果封裝的比較好,只是想改變一點(diǎn)功能,但是沒有公開屬性.這種情況可以使用 runtime配合KVC 進(jìn)行修改.
這種寫法我在UITextField中Placeholder的問題(附:常用屬性和方法)介紹過, 那里是用的 swift 的語法.這里使用 OC

- (void)getIvars:(Class)class {
    unsigned int count = 0;
    Ivar *ivas = class_copyIvarList(class, &count);

    for (int i = 0; i< count; i++) {
        Ivar ivar = ivas[i];
        const char *name = ivar_getName(ivar);
        const char *type = ivar_getTypeEncoding(ivar);
        NSString *ivarName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
        NSString *ivarType = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
        NSLog(@"%@--%@", ivarType, ivarName);
    }
}

- (void)getPropertyList:(Class)class {
    unsigned int count = 0;
    objc_property_t *pros = class_copyPropertyList(class, &count);
    
       for (int i = 0; i< count; i++) {
           objc_property_t pro = pros[i];
           const char *name = property_getName(pro);
           const char *type = property_getAttributes(pro);
           NSString *proName = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
           NSString *proType = [NSString stringWithCString:type encoding:NSUTF8StringEncoding];
           NSLog(@"%@--%@", proType, proName);
       }
}

這樣子可以拿到系統(tǒng)的屬性,包括一些. m 中私有的.當(dāng)然借助于上面的方法我們也可以很好的進(jìn)行字典轉(zhuǎn)模型了.

+ (instancetype)modelWithDict:(NSDictionary *)dict {
    // 創(chuàng)建對應(yīng)類的對象
    id objc = [[self alloc] init];
    /*
     遍歷模型所有成員屬性
     ivar:成員屬性
     class_copyIvarList:把成員屬性列表復(fù)制一份
     Ivar *:指向Ivar指針,指向一個(gè)成員變量數(shù)組
     class:獲取哪個(gè)類的成員屬性列表
     count:成員屬性總數(shù)
    */
    unsigned int count = 0;
    Ivar *ivarList = class_copyIvarList(self, &count);
    for (int i = 0 ; i < count; i++) {
        // 獲取成員屬性
        Ivar ivar = ivarList[i];
        // 獲取成員名
       NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        // 成員屬性類型
        NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];

        // 獲取key,由于propertyName是帶有下劃線(_)的變量名
        NSString *key = [propertyName substringFromIndex:1];
        // 獲取字典的value
        id value = dict[key];        
        if (value) {
            [objc setValue:value forKey:key];
        }
    }
    return objc;
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末姑裂,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子男旗,更是在濱河造成了極大的恐慌舶斧,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件察皇,死亡現(xiàn)場離奇詭異茴厉,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門矾缓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來师痕,“玉大人,你說我怎么就攤上這事而账。” “怎么了因篇?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵泞辐,是天一觀的道長。 經(jīng)常有香客問我竞滓,道長咐吼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任商佑,我火速辦了婚禮锯茄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘茶没。我一直安慰自己肌幽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布抓半。 她就那樣靜靜地躺著喂急,像睡著了一般。 火紅的嫁衣襯著肌膚如雪笛求。 梳的紋絲不亂的頭發(fā)上廊移,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天,我揣著相機(jī)與錄音探入,去河邊找鬼狡孔。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蜂嗽,可吹牛的內(nèi)容都是我干的苗膝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼植旧,長吁一口氣:“原來是場噩夢啊……” “哼荚醒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起隆嗅,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤界阁,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后胖喳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泡躯,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了较剃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片咕别。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖写穴,靈堂內(nèi)的尸體忽然破棺而出惰拱,到底是詐尸還是另有隱情,我是刑警寧澤啊送,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布偿短,位于F島的核電站,受9級特大地震影響馋没,放射性物質(zhì)發(fā)生泄漏昔逗。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一篷朵、第九天 我趴在偏房一處隱蔽的房頂上張望勾怒。 院中可真熱鬧,春花似錦声旺、人聲如沸笔链。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽卡乾。三九已至,卻和暖如春缚够,著一層夾襖步出監(jiān)牢的瞬間幔妨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工谍椅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留误堡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓雏吭,卻偏偏與公主長得像锁施,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子杖们,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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

  • 對于從事 iOS 開發(fā)人員來說悉抵,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,721評論 7 64
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,554評論 33 466
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,135評論 0 9
  • Runtime是什么 Runtime 又叫運(yùn)行時(shí),是一套底層的 C 語言 API摘完,其為 iOS 內(nèi)部的核心之一姥饰,我...
    SuAdrenine閱讀 876評論 0 3
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認(rèn)知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 1,939評論 1 3