runtime

什么是runtime沾乘?

1> runtime是一套底層的C語言API(包含很多強(qiáng)大實(shí)用的C語言數(shù)據(jù)類型、C語言函數(shù))
2> 實(shí)際上积蔚,平時(shí)我們編寫的OC代碼意鲸,底層都是基于runtime實(shí)現(xiàn)的

  • 也就是說,平時(shí)我們編寫的OC代碼尽爆,最終都是轉(zhuǎn)成了底層的runtime代碼(C語言代碼)

runtime有啥用怎顾?

1> 能動(dòng)態(tài)產(chǎn)生一個(gè)類、一個(gè)成員變量漱贱、一個(gè)方法
2> 能動(dòng)態(tài)修改一個(gè)類槐雾、一個(gè)成員變量、一個(gè)方法
3> 能動(dòng)態(tài)刪除一個(gè)類幅狮、一個(gè)成員變量募强、一個(gè)方法

常見的函數(shù)株灸、頭文件

成員變量、類擎值、方法
#import <objc/runtime.h>

獲得某個(gè)類內(nèi)部的所有成員變量
Ivar * class_copyIvarList

獲得某個(gè)類內(nèi)部的所有方法
Method * class_copyMethodList

獲得某個(gè)實(shí)例方法(對(duì)象方法慌烧,減號(hào)-開頭)
Method class_getInstanceMethod

獲得某個(gè)類方法(加號(hào)+開頭)
Method class_getClassMethod

交換2個(gè)方法的具體實(shí)現(xiàn)
method_exchangeImplementations

消息機(jī)制
#import <objc/message.h> objc_msgSend(....)

  • @param receiver 消息接收者
  • @param selector 消息對(duì)應(yīng)的方法名字
  • @param arg1.arg2...消息中的任意數(shù)目的參數(shù)
    objc_msgSend(receiver, selector, arg1, arg2, ...)

獲取某個(gè)類的所有成員變量(延伸作用是歸檔、解檔)

- (void)testRuntimeIvar
{
    // Ivar : 成員變量
    unsigned int count = 0;
    // 獲得所有的成員變量
    //指針可以理解成為數(shù)組(根據(jù)1鸠儿、2屹蚊、3進(jìn)行取值)
    Ivar *ivars = class_copyIvarList([HMPerson class], &count);
    for (int i = 0; i<count; i++) {
        // 取得i位置的成員變量
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        const char *type = ivar_getTypeEncoding(ivar);
        NSLog(@"%d %s %s", i, name, type);
    }
    
    //    HMPerson *p = [[HMPerson alloc] init];
    //    objc_msgSend(p, @selector(setAge:), 20);
    
    //    NSLog(@"%d", p.age);
}

歸檔示例

- (void)encodeWithCoder:(NSCoder *)encoder
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([HMPerson class], &count);
    for (int i = 0; i<count; i++) {
        // 取得i位置的成員變量
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        NSString *key = [NSString stringWithUTF8String:name];
        [encoder encodeObject:[self valueForKeyPath:key] forKey:key];
    }
}

交換兩個(gè)方法的實(shí)現(xiàn)

交換方法
OBJC_EXPORT void method_exchangeImplementations(Method m1, Method m2)

獲得方法
Method class_getClassMethod(Class cls, SEL name)

例:

  • 分類加載進(jìn)內(nèi)存的時(shí)候會(huì)調(diào)用load方法,只會(huì)調(diào)用一次进每。當(dāng)使用這個(gè)方法的時(shí)候汹粤,不需要聲明也不需要寫.h方法。因?yàn)樗?code>.m方法都會(huì)加入內(nèi)存中田晚,調(diào)用到load中嘱兼。

  • 實(shí)現(xiàn)了imageWithName:imageNamed:方法的交換,需要注意的問題是再重寫imageWithName:時(shí)候不要在內(nèi)部使用imageNamed:方法

#import <objc/runtime.h>

@implementation UIImage (Extension)
/**
 *  只要分類被裝載到內(nèi)存中贤徒,就會(huì)調(diào)用1次
 */
+ (void)load
{
    Method otherMehtod = class_getClassMethod(self, @selector(imageWithName:));
    Method originMehtod = class_getClassMethod(self, @selector(imageNamed:));
    // 交換2個(gè)方法的實(shí)現(xiàn)
    method_exchangeImplementations(otherMehtod, originMehtod);
}

+ (UIImage *)imageWithName:(NSString *)name
{
    BOOL iOS7 = [[UIDevice currentDevice].systemVersion floatValue] >= 7.0;
    UIImage *image = nil;
    if (iOS7) {
        NSString *newName = [name stringByAppendingString:@"_os7"];
        image = [UIImage imageWithName:newName];
    }
    
    if (image == nil) {
        image = [UIImage imageWithName:name];
    }
    return image;
}

獲得方法地址

  • 編譯時(shí)沒問題芹壕,運(yùn)行時(shí)才執(zhí)行相應(yīng)的方法,這就是所謂的動(dòng)態(tài)綁定泞莉。

避免動(dòng)態(tài)綁定的唯一辦法就是取得方法的地址,并且直接象函數(shù)調(diào)用一樣調(diào)用它哪雕。當(dāng)一個(gè)方法會(huì)被連續(xù)調(diào) 用很多次,而且您希望節(jié)省每次調(diào)用方法都要發(fā)送消息的開銷時(shí),使用方法地址來調(diào)用方法就顯得很有效。

利用 Cocoa 運(yùn)行時(shí)系統(tǒng)的提供的功能 methodForSelector: 實(shí)現(xiàn)某個(gè)方法


    
    void (*setter)(id, SEL, BOOL);
    
    setter = (void (*)(id, SEL, BOOL))[target methodForSelector:@selector(setFilled)];
    
    for (NSInteger i = 0; i < 100; i++) {
        setter(target,@selector(setFilled),YES);
    }

- (void)setFilled
{
    NSLog(@"123");
}

使用 methodForSelector:來避免動(dòng)態(tài)綁定將減少大部分消息的開銷,但是這只有在指定的消息被重 復(fù)發(fā)送很多次時(shí)才有意義,例如上面的 for 循環(huán)鲫趁。
注意,methodForSelector:Cocoa運(yùn)行時(shí)系統(tǒng)的提供的功能,而不是Objective-C語言本身的功 能斯嚎。

消息機(jī)制

消息
消息

當(dāng)對(duì)象收到消息時(shí),消息函數(shù)首先根據(jù)該對(duì)象的isa指針找到該對(duì)象所對(duì)應(yīng)的類的方法表,并從表中尋找 該消息對(duì)應(yīng)的方法選標(biāo)。如果找不到,objc_msgSend 將繼續(xù)從父類中尋找,直到 NSObject 類挨厚。一 旦找到了方法選標(biāo), objc_msgSend 則以消息接收者對(duì)象為參數(shù)調(diào)用,調(diào)用該選標(biāo)對(duì)應(yīng)的方法實(shí)現(xiàn)堡僻。
這就是在運(yùn)行時(shí)系統(tǒng)中選擇方法實(shí)現(xiàn)的方式吸重。在面向?qū)ο缶幊讨?一般稱作方法和消息動(dòng)態(tài)綁定的過程又碌。

為了加快消息的處理過程,運(yùn)行時(shí)系統(tǒng)通常會(huì)將使用過的方法選標(biāo)和方法實(shí)現(xiàn)的地址放入緩存中巍扛。每個(gè)類
都有一個(gè)獨(dú)立的緩存,同時(shí)包括繼承的方法和在該類中定義的方法询枚。消息函數(shù)會(huì)首先檢查消息接收者對(duì)象
對(duì)應(yīng)的類的緩存(理論上,如果一個(gè)方法被使用過一次,那么它很可能被再次使用)。如果在緩存中已經(jīng)
有了需要的方法選標(biāo),則消息僅僅比函數(shù)調(diào)用慢一點(diǎn)點(diǎn)商蕴。如果程序運(yùn)行了足夠長(zhǎng)的時(shí)間,幾乎每個(gè)消息都
能在緩存中找到方法實(shí)現(xiàn)泻红。程序運(yùn)行時(shí),緩存也將隨著新的消息的增加而增加负敏。

消息轉(zhuǎn)發(fā)

如果一個(gè)對(duì)象收到一條無法處理的消息,運(yùn)行時(shí)系統(tǒng)會(huì)在拋出錯(cuò)誤前,給該對(duì)象發(fā)送一條
forwardInvocation:消息,該消息的唯一參數(shù)是個(gè) NSInvocation 類型的對(duì)象——該對(duì)象封裝了 原始的消息和消息的參數(shù)壤躲。

防止crash


- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    
    //Person實(shí)現(xiàn)了`invocation selector`方法
    return [Person instanceMethodSignatureForSelector:aSelector];
    
    //傳遞參數(shù)到forwardInvocation: 的invocation的key,采用的是傳遞方式的辦法
    //   return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    
}


- (void)forwardInvocation:(NSInvocation *)invocation
{
    Person *person = [[Person alloc] init];
//
//    NSString *key = NSStringFromSelector([invocation selector]);
    
     NSString *sel = NSStringFromSelector([invocation selector]);
     NSLog(@"%@",sel);
    
    
    if ([person respondsToSelector:
         [invocation selector]])
        [invocation invokeWithTarget:person];
    
    else
        NSLog(@"nothing");
    //程序會(huì)crash
//    [super forwardInvocation:invocation]
}

參考資料:

鏈接: [http://pan.baidu.com/s/1i3JB56d][jekyll - DB] 密碼: cmh6
[jekyll - DB]: http://pan.baidu.com/s/1i3JB56d

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末城菊,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子碉克,更是在濱河造成了極大的恐慌凌唬,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,888評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件漏麦,死亡現(xiàn)場(chǎng)離奇詭異客税,居然都是意外死亡况褪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門更耻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來测垛,“玉大人,你說我怎么就攤上這事秧均〈蜕矗” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵熬北,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我诚隙,道長(zhǎng)讶隐,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評(píng)論 1 297
  • 正文 為了忘掉前任久又,我火速辦了婚禮巫延,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘地消。我一直安慰自己炉峰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評(píng)論 6 397
  • 文/花漫 我一把揭開白布脉执。 她就那樣靜靜地躺著疼阔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪半夷。 梳的紋絲不亂的頭發(fā)上婆廊,一...
    開封第一講書人閱讀 52,337評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音巫橄,去河邊找鬼淘邻。 笑死,一個(gè)胖子當(dāng)著我的面吹牛湘换,可吹牛的內(nèi)容都是我干的宾舅。 我是一名探鬼主播,決...
    沈念sama閱讀 40,902評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼彩倚,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼筹我!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起署恍,我...
    開封第一講書人閱讀 39,807評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤崎溃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后盯质,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體袁串,經(jīng)...
    沈念sama閱讀 46,349評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡概而,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了囱修。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赎瑰。...
    茶點(diǎn)故事閱讀 40,567評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖破镰,靈堂內(nèi)的尸體忽然破棺而出餐曼,到底是詐尸還是另有隱情,我是刑警寧澤鲜漩,帶...
    沈念sama閱讀 36,242評(píng)論 5 350
  • 正文 年R本政府宣布源譬,位于F島的核電站,受9級(jí)特大地震影響孕似,放射性物質(zhì)發(fā)生泄漏踩娘。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評(píng)論 3 334
  • 文/蒙蒙 一喉祭、第九天 我趴在偏房一處隱蔽的房頂上張望养渴。 院中可真熱鬧,春花似錦泛烙、人聲如沸理卑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藐唠。三九已至,卻和暖如春孵滞,著一層夾襖步出監(jiān)牢的瞬間中捆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工坊饶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泄伪,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,995評(píng)論 3 377
  • 正文 我出身青樓匿级,卻偏偏與公主長(zhǎng)得像蟋滴,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子痘绎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評(píng)論 2 359

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