Runtime 見(jiàn)聞?wù)?/h1>

Runtime

基本是用C和匯編寫(xiě)的
Runtime 涉及三個(gè)點(diǎn)罢缸,面向?qū)ο箐桃颍⒎职l(fā)呵俏,消息轉(zhuǎn)發(fā)

面向?qū)ο?/h4>

Objective-C 的對(duì)象是基于 Runtime 創(chuàng)建的結(jié)構(gòu)體

image

消息分發(fā)

    // 創(chuàng)建person對(duì)象
    Person *p = [[Person alloc] init];
    
    // 調(diào)用對(duì)象方法
    [p eat];
    
    // 本質(zhì):讓對(duì)象發(fā)送消息
    objc_msgSend(p, @selector(eat));

    // 調(diào)用類方法的方式:兩種
    // 第一種通過(guò)類名調(diào)用
    [Person eat];
    // 第二種通過(guò)類對(duì)象調(diào)用
    [[Person class] eat];
    
    // 用類名調(diào)用類方法畔师,底層會(huì)自動(dòng)把類名轉(zhuǎn)換成類對(duì)象調(diào)用
    // 本質(zhì):讓類對(duì)象發(fā)送消息
    objc_msgSend([Person class], @selector(eat));

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

完整的消息轉(zhuǎn)發(fā)流程圖:


image
  • 1薄霜、動(dòng)態(tài)方法解析
  • +(BOOL)resolveInstanceMethod:(SEL)sel;

當(dāng)接受到未能識(shí)別的SEL時(shí),運(yùn)行時(shí)系統(tǒng)會(huì)調(diào)用該函數(shù)用以給對(duì)象一次機(jī)會(huì)來(lái)添加相應(yīng)的方法實(shí)現(xiàn)漱凝,如果用戶在該函數(shù)中動(dòng)態(tài)添加了相應(yīng)方法的實(shí)現(xiàn)疮蹦,則跳轉(zhuǎn)到方法的實(shí)現(xiàn)部分,并將該實(shí)現(xiàn)存入緩存中碉哑,以供下次調(diào)用挚币。

  • 2、備援接收者
  • -(id)forwardingTargetForSelector:(SEL)aSelector;

如果運(yùn)行時(shí)在消息轉(zhuǎn)發(fā)的第一步中未找到所調(diào)用方法的實(shí)現(xiàn)扣典,那么當(dāng)前接收者還有第二次機(jī)會(huì)進(jìn)行未知SEL的處理妆毕。這時(shí)運(yùn)行期系統(tǒng)會(huì)調(diào)用上述方法,并將未知SEL作為參數(shù)傳入贮尖,該方法可以返回一個(gè)能處理該選擇子的對(duì)象笛粘,運(yùn)行時(shí)系統(tǒng)會(huì)根據(jù)返回的對(duì)象進(jìn)行查找,若找到則跳轉(zhuǎn)到相應(yīng)方法的實(shí)現(xiàn)湿硝,則消息轉(zhuǎn)發(fā)結(jié)束薪前。

  • 3、完整的消息轉(zhuǎn)發(fā)
  • -(void)forwardInvocation:(NSInvocation *)anInvocation;

當(dāng)運(yùn)行時(shí)系統(tǒng)檢測(cè)到第二步中用戶未返回能處理相應(yīng)選擇子的對(duì)象時(shí)关斜,那么來(lái)到這一步就要啟動(dòng)完整的消息轉(zhuǎn)發(fā)機(jī)制了示括。該方法可以改變消息調(diào)用目標(biāo),運(yùn)行時(shí)系統(tǒng)根據(jù)所改變的調(diào)用目標(biāo)痢畜,向調(diào)用目標(biāo)方法列表中查詢對(duì)應(yīng)方法的實(shí)現(xiàn)并實(shí)現(xiàn)跳轉(zhuǎn)垛膝,這種方式和第二步的操作非常相似。當(dāng)然你也可以修改方法的選擇子丁稀,亦或者向所調(diào)用方法中追加一個(gè)參數(shù)等來(lái)跳轉(zhuǎn)到相關(guān)方法的實(shí)現(xiàn)吼拥。

最后,如果消息轉(zhuǎn)發(fā)的第三步還未能處理該未知選擇子的話线衫,那么最終會(huì)調(diào)用NSObject類的如下方法用以異常的拋出凿可,表明該選擇子最終未能處理。

  • -(void)doesNotRecognizeSelector:(SEL)aSelector;

常用方式

替換系統(tǒng)方法

獲得某個(gè)類的類方法
Method class_getClassMethod(Class cls , SEL name)
獲得某個(gè)類的實(shí)例對(duì)象方法
Method class_getInstanceMethod(Class cls , SEL name)
交換兩個(gè)方法的實(shí)現(xiàn)
void method_exchangeImplementations(Method m1 , Method m2)

攔截系統(tǒng)方法

1.建一個(gè)分類(UIImage+Category)
2.分類中實(shí)現(xiàn)一個(gè)自定義方法授账,方法中寫(xiě)要在系統(tǒng)方法中加入的語(yǔ)句

+ (UIImage *)xh_imageNamed:(NSString *)name {
double version = [[UIDevice currentDevice].systemVersion doubleValue];
if (version >= 7.0) {
    // 如果系統(tǒng)版本是7.0以上枯跑,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片
    name = [name stringByAppendingString:@"_os7"];
}
return [UIImage xh_imageNamed:name];
}

3.分類中重寫(xiě)UIImage的load方法惨驶,實(shí)現(xiàn)方法的交換

+ (void)load {
    // 獲取兩個(gè)類的類方法
    Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:));
    Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:));
    // 開(kāi)始交換方法實(shí)現(xiàn)
    method_exchangeImplementations(m1, m2);
}

實(shí)現(xiàn)分類增加屬性(關(guān)聯(lián)對(duì)象)

.h
@property(nonatomic,copy)NSString *name;
.m
static NSString * const nameKey = @"nameKey";
- (void)setName:(NSString *)name {
    // 將某個(gè)值跟某個(gè)對(duì)象關(guān)聯(lián)起來(lái),將某個(gè)值存儲(chǔ)到某個(gè)對(duì)象中
    objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
 
- (NSString *)name {
    return objc_getAssociatedObject(self, &nameKey);
}

自動(dòng)歸檔解檔

自定義對(duì)象不能用于writeToFile保存敛助,需要用于歸檔來(lái)進(jìn)行保存

NSObject+Extension

- (void)encodeWithCoder:(NSCoder *)encoder
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([Movie 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];
        [encoder encodeObject:value forKey:key];
    }
    free(ivars);
}

- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([Movie 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 = [decoder decodeObjectForKey:key];
       // 設(shè)置到成員變量身上
        [self setValue:value forKey:key];

        }
        free(ivars);
    } 
    return self;
}
@end

字典敞咧、Json、Model轉(zhuǎn)換

  //字典數(shù)據(jù)轉(zhuǎn)Model
  
User *user = [[User alloc] init];
    
//獲取當(dāng)前model所有屬性
unsigned int propertiesCount;
objc_property_t *properties = class_copyPropertyList([user class], &propertiesCount);

for (NSUInteger i = 0; i < propertiesCount; i++){
     //獲取property Name
    objc_property_t property = properties[i];
    const char *propertyName = property_getName(property);
    id value = [json valueForKey:@(propertyName)];
    //使用KVC形式進(jìn)行對(duì)象賦值
    [user performSelector:@selector(setValue:forKey:) withObject:value withObject:@(propertyName)];
}

上述代碼僅僅可以做到NSDictionary轉(zhuǎn)Model辜腺,并且Model不能包含自定義對(duì)象
完整代碼可能需要單獨(dú)來(lái)介紹---->Runtime 字典、Json乍恐、Model(含內(nèi)嵌對(duì)象)轉(zhuǎn)換

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

  • 開(kāi)發(fā)使用場(chǎng)景:如果一個(gè)類方法非常多评疗,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源,需要給每個(gè)方法生成映射表茵烈,可以使用動(dòng)態(tài)給某個(gè)類百匆,添加方法解
  • 經(jīng)典面試題:怎么調(diào)用類的私有方法?有沒(méi)有使用performSelector呜投,其實(shí)主要想問(wèn)你有沒(méi)有動(dòng)態(tài)添加過(guò)方法加匈。
@interface ViewController ()
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    Person *p = [[Person alloc] init];
    //還可以調(diào)用私有方法
    [p performSelector:@selector(run:) withObject:@10];
}
@end
#import "Person.h"
#import <objc/message.h>

@implementation Person

// 沒(méi)有返回值,也沒(méi)有參數(shù) -->  void,(id,SEL)
void aaa(id self, SEL _cmd, NSNumber *meter) {
    NSLog(@"跑了%@", meter); 
}

// 任何方法默認(rèn)都有兩個(gè)隱式參數(shù),self,_cmd(_cmd代表方法編號(hào),打印結(jié)果為當(dāng)前執(zhí)行的方法名)
// 什么時(shí)候調(diào)用:只要一個(gè)對(duì)象調(diào)用了一個(gè)未實(shí)現(xiàn)的方法就會(huì)調(diào)用這個(gè)方法,進(jìn)行處理
// 作用:動(dòng)態(tài)添加方法,處理未實(shí)現(xiàn)
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == NSSelectorFromString(@"run:")) {
        //aaa不會(huì)生成方法列表
        class_addMethod(self, sel, (IMP)aaa, "v@:@");
       
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

@end

動(dòng)態(tài)變量控制

在程序中仑荐,Person的私有屬性age是10雕拼,使用runtime變成了20。

Person *p = [[Person alloc] init];
Ivar *ivar = class_copyIvarList([p class], &count);
const char *varName = ivar_getName(var);
object_setIvar(p, var, @"20");

實(shí)現(xiàn)萬(wàn)能調(diào)整(Router)

- (void)push:(NSString *)classFromString{

    Class actionClass = NSClassFromString(classFromString);
    id target = [[actionClass alloc] init];

    if(!target){
        NSLog(@"沒(méi)有當(dāng)前類");
    }

    if (![(NSObject *) target isKindOfClass:[UIViewController class]]) {
        NSLog(@"當(dāng)前不是UIViewController");
    }

    UINavigationController *navigationCtr = [self getActiveNavigationController];
    if ([navigationCtr isKindOfClass:[UINavigationController class]]) {
        [navigationCtr pushViewController:(UIViewController *)target animated:YES];
    }
}

- (UINavigationController *)getActiveNavigationController
{
    if ([[UIApplication sharedApplication] delegate].window.isKeyWindow)
    {
        UIViewController *rootViewController = [UIApplication sharedApplication].keyWindow.rootViewController;
        if ([rootViewController isKindOfClass:[UITabBarController class]])
        {
            UINavigationController *navigationController = ((UITabBarController *) rootViewController).selectedViewController;
            return navigationController;
        } else if ([rootViewController isKindOfClass:[UINavigationController class]])
            return (UINavigationController *) rootViewController;
        else
            return nil;

    } else
        return nil;
}

by 有涯sui無(wú)涯

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者

  • 序言:七十年代末粘招,一起剝皮案震驚了整個(gè)濱河市啥寇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌洒扎,老刑警劉巖辑甜,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異袍冷,居然都是意外死亡磷醋,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)胡诗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)邓线,“玉大人,你說(shuō)我怎么就攤上這事乃戈」犹担” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵症虑,是天一觀的道長(zhǎng)缩歪。 經(jīng)常有香客問(wèn)我,道長(zhǎng)谍憔,這世上最難降的妖魔是什么匪蝙? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任主籍,我火速辦了婚禮,結(jié)果婚禮上逛球,老公的妹妹穿的比我還像新娘千元。我一直安慰自己,他們只是感情好颤绕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布幸海。 她就那樣靜靜地躺著,像睡著了一般奥务。 火紅的嫁衣襯著肌膚如雪物独。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,688評(píng)論 1 305
  • 那天氯葬,我揣著相機(jī)與錄音挡篓,去河邊找鬼。 笑死帚称,一個(gè)胖子當(dāng)著我的面吹牛官研,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播闯睹,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼戏羽,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了楼吃?” 一聲冷哼從身側(cè)響起蛛壳,我...
    開(kāi)封第一講書(shū)人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎所刀,沒(méi)想到半個(gè)月后衙荐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡浮创,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年忧吟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斩披。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡溜族,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出垦沉,到底是詐尸還是另有隱情煌抒,我是刑警寧澤,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布厕倍,位于F島的核電站寡壮,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜况既,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一这溅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧棒仍,春花似錦悲靴、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至乱陡,卻和暖如春否纬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背蛋褥。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留睛驳,地道東北人烙心。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像乏沸,于是被迫代替她去往敵國(guó)和親淫茵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

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