OC(九):runtime機制簡單理解和應(yīng)用

寫在前面

runtime 是底層的東西,是 OC 的根基,叫做"動態(tài)運行時",運行過程中將 OC 轉(zhuǎn)化為 C,在程序運行時候提供服務(wù),如果消息傳遞,動態(tài)添加屬性,動態(tài)添加方法 以及重要的 KVC 的dictionary轉(zhuǎn) model.
程序??必啃的骨頭,來一起下嘴,啃~~

一,使用 runtime 進(jìn)行 KVC 映射賦值

KVC 的實現(xiàn)原理為在 model 中查找相應(yīng)的方法進(jìn)行賦值,順序如下:


KVC 原理

如果上面的圖(微信隨便畫了一個??)無法理解,就看下面的一幅圖.由于 簡書的Markdown 還不支持流程圖,無法得到正常的流程圖,就在別的編輯器寫好,截取過來了


Markdown 的流程圖

附上 Markdown語法如下:

st=>start: Start

cond=>condition: 查找 setValue
cond2=>condition: 查找 _value
cond3=>condition: 查找 value
sub=>subroutine: KVC 轉(zhuǎn)換為 model

e=>end

st->cond
cond(no)->cond2(no)->cond3
cond(yes)->sub->e
cond2(yes)->sub->e
cond3(yes)->sub->e

明白了這個,就開始寫代碼.
創(chuàng)建 NSObject 的 Category, 定義類方法

.h

//
//  NSObject+CategoryOfNSObject.h
//  Runtime
//
//  Created by 宋金委 on 2016/10/21.
//  Copyright ? 2016年 song. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface NSObject (CategoryOfNSObject)
/**
 初始化一個 Model
 
 @param dict 字典
 */
+(instancetype)initModelWithDictionary:(NSDictionary *)dict;
@end

.m

//
//  NSObject+CategoryOfNSObject.m
//  Runtime
//
//  Created by 宋金委 on 2016/10/21.
//  Copyright ? 2016年 song. All rights reserved.
//

#import "NSObject+CategoryOfNSObject.h"
#import <objc/runtime.h>

@implementation NSObject (CategoryOfNSObject)


+(instancetype)initModelWithDictionary:(NSDictionary *)dict{
    
    id model = [[self alloc] init];
    //變量個數(shù)
    unsigned int numberOfVariable = 0;
    //成員變量的數(shù)組
    Ivar* ivarList = class_copyIvarList([self class], &numberOfVariable);
    
    for (int i = 0 ; i < numberOfVariable; i++) {
        Ivar singleVariable = ivarList[i];
        
        //成員變量類型 格式:\"type\"
        NSString* variableType = [NSString stringWithUTF8String:ivar_getTypeEncoding(singleVariable)];
        variableType = [variableType stringByReplacingOccurrencesOfString:@"@" withString:@""];
        variableType = [variableType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        //成員變量名字 格式:_name
        NSString* variableName = [NSString stringWithUTF8String:ivar_getName(singleVariable)];
        
        //key "_name"-->"name"
        NSString* key = [variableName substringFromIndex:1];
        
        //獲取字典對應(yīng) key的值
        id valueOfKeyInDict = dict[key];
        
        if (![valueOfKeyInDict isKindOfClass:[NSDictionary class]] && ![variableType hasPrefix:@"NS"]) {
            
            Class classOfVariable = NSClassFromString(variableType);
            
            valueOfKeyInDict = [classOfVariable initModelWithDictionary:valueOfKeyInDict];
            
        }
        if(valueOfKeyInDict){
            
            [model setValue:valueOfKeyInDict forKey:key];
        }
        
    }
    //很重要
    free(ivarList);
    
    return model;
}

@end

簡單測試調(diào)用代碼

//
//  ViewController.m
//  Runtime
//
//  Created by 宋金委 on 2016/10/20.
//  Copyright ? 2016年 song. All rights reserved.
//

#import "ViewController.h"
#import "NSObject+CategoryOfNSObject.h"
#import "Model.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    Model* model = [Model initModelWithDictionary:@{@"name":@"Song",@"age":@"22",@"address":@"China"}];
    
    NSLog(@"----%@",model);
}
@end

測試結(jié)果:


debug 的內(nèi)存顯示

二,消息機制

在 OC中調(diào)用方法時候,是被clang 編譯器 cpp,terminal可以輸入 "clang -rewrite-objc 文件名",可以生成. cpp 文件,可以看到方法調(diào)用工程中被編譯為 objc_msgSend("className","methodName");

例如:只是簡寫過后的樣子

NSArray * array = [[NSArray alloc] init];
//最底層的寫法
==>  NSArray * array = objc_msgSend(getClass("NSArray"),sel_registerName("alloc"));
//另外一種方法
==> array =  objc_msgSend(array,@selector(init));

應(yīng)用場景:當(dāng)某個方法沒有暴露,可以使用 obj_msgSend 調(diào)用;
方法調(diào)用工程: 編譯工程時候,會將對象的方法,轉(zhuǎn)化為方法編號,放到對象的方法列表(hash 表),在方法被調(diào)用的過程中,發(fā)送消息,通過類對象的 isa 指針查找方法是否存在,如果存在,映射對應(yīng)的方法編號,在代碼區(qū)查找對應(yīng)的實現(xiàn),調(diào)用方法.

三,替換方法

//
//  NSMutableArray+CategoryOfNSMutableArray.m
//  Runtime
//
//  Created by HMC on 2016/10/24.
//  Copyright ? 2016年 song. All rights reserved.
//

#import "NSMutableArray+CategoryOfNSMutableArray.h"
#import <objc/runtime.h>

@implementation NSMutableArray (CategoryOfNSMutableArray)

//第一次加載的時候執(zhí)行一次
+(void)load{

    Method addObject = class_getInstanceMethod(self, @selector(addObject:));
    
    Method song_addObject = class_getInstanceMethod(self, @selector(song_addObject:));

    method_exchangeImplementations(addObject, song_addObject);
    
}

-(void)song_addObject:(id)anObject {
    
    NSLog(@"插入對象不能為空");
    if (anObject == nil) {
        
        NSLog(@"插入對象不能為空");
    }else{
    
        [self song_addObject:anObject];
    }
}
@end

調(diào)用代碼,
還是按原來的方法調(diào)用,替換方法只是在分類中是系統(tǒng)直接替換的.

    NSMutableArray * ar = [NSMutableArray array];
    
    NSString * s = @"123";
    
    [ar addObject:s];

四,動態(tài)添加對象屬性

其實就是在分類中重寫該屬性的get/set 方法,在方法中使用 runtime 添加或者獲取值

//
//  UIAlertView+CategoryOfUIAlertView.m
//  Runtime
//
//  Created by 宋金委 on 2016/10/24.
//  Copyright ? 2016年 song. All rights reserved.
//

#import "UIAlertView+CategoryOfUIAlertView.h"
#import <objc/runtime.h>
@implementation UIAlertView (CategoryOfUIAlertView)


-(NSNumber *)intervalTime{

    return objc_getAssociatedObject(self, @"intervalTime");
}

-(void)setIntervalTime:(NSNumber *)intervalTime{

    objc_setAssociatedObject(self, @"intervalTime", intervalTime, OBJC_ASSOCIATION_ASSIGN);
}


@end
顯示時間為100

五,動態(tài)添加對象方法

應(yīng)用場景:當(dāng)一個 APP 提供增值服務(wù),比如開通會員,可以提供會員服務(wù),就會響應(yīng)相應(yīng)的方法;
實現(xiàn)原理: 當(dāng)一個 instance 通過performSelector(methodName)方法調(diào)用methodName 時,如果檢測每一個實現(xiàn)該方法就會執(zhí)行+(BOOL)resolveInstanceMethod:(SEL)sel這個類方法,我們在此方法中運用 runtime 進(jìn)行動態(tài)添加方法,反之就會 crash.

//
//  User.m
//  Runtime
//
//  Created by 宋金委 on 2016/10/24.
//  Copyright ? 2016年 song. All rights reserved.
//

#import "User.h"
#import <objc/runtime.h>

@implementation User

void VIPSeriviceImp(id self, SEL _cmd ,NSNumber* num ){

    NSLog(@"您開通的是%@元的 VIP",num);
}


+(BOOL)resolveInstanceMethod:(SEL)sel{

    if (sel == NSSelectorFromString(@"VIPSerivice:")) {
        
        /**
         runtime 動態(tài)添加方法

         @param self 對象本身
         @param sel  SEL
         @param IMP  sel 的實現(xiàn)(C 函數(shù))
         @param string C函數(shù)格式

         @return BOOL
         */
        class_addMethod(self, sel, (IMP)VIPSeriviceImp, "v@:@");
        
    }
    
    return [super resolveInstanceMethod:sel];
}

@end

class_addMethod第四個參數(shù)的官方對照表

動態(tài)調(diào)用方法performSelector ,會報警告??,不用管,這是編譯器的代碼檢測功能引起的.

//動態(tài)添加方法
    User * user = [User new];
    [user performSelector:@selector(VIPSerivice:) withObject:@10];

代碼地址: 點這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贫导,一起剝皮案震驚了整個濱河市贼陶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碉怔,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異上荡,居然都是意外死亡,警方通過查閱死者的電腦和手機叁征,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門逛薇,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人永罚,你說我怎么就攤上這事∧馗ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵惕蹄,是天一觀的道長治专。 經(jīng)常有香客問我,道長泪蔫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任鸥滨,我火速辦了婚禮,結(jié)果婚禮上婿滓,老公的妹妹穿的比我還像新娘。我一直安慰自己橘券,他們只是感情好卿吐,可當(dāng)我...
    茶點故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布淑玫。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪盏缤。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天竹捉,我揣著相機與錄音,去河邊找鬼块差。 笑死倔丈,一個胖子當(dāng)著我的面吹牛憨闰,可吹牛的內(nèi)容都是我干的乃沙。 我是一名探鬼主播,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼训裆,長吁一口氣:“原來是場噩夢啊……” “哼蜀铲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起记劝,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎定欧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砍鸠,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年录豺,在試婚紗的時候發(fā)現(xiàn)自己被綠了饭弓。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡咏花,死狀恐怖阀趴,靈堂內(nèi)的尸體忽然破棺而出迟螺,到底是詐尸還是另有隱情舍咖,我是刑警寧澤锉桑,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站攻柠,受9級特大地震影響后裸,放射性物質(zhì)發(fā)生泄漏瑰钮。R本人自食惡果不足惜微驶,卻給世界環(huán)境...
    茶點故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望苟耻。 院中可真熱鬧扶檐,春花似錦、人聲如沸款筑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至毛秘,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間叫挟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工员凝, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留奋献,地道東北人。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓糖埋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瞳别。 傳聞我的和親對象是個殘疾皇子杭攻,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,086評論 2 355

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,721評論 0 9
  • 對于從事 iOS 開發(fā)人員來說馆铁,所有的人都會答出【runtime 是運行時】什么情況下用runtime?大部分人能...
    夢夜繁星閱讀 3,722評論 7 64
  • 目錄 Objective-C Runtime到底是什么 Objective-C的元素認(rèn)知 Runtime詳解 應(yīng)用...
    Ryan___閱讀 1,939評論 1 3
  • 我在咸冷一樣的海水里 孤獸一樣的遠(yuǎn)方 隔壁睡了我隔壁的娘子 血液把我從黑夜驚醒 多少次我們是沉默的鬼 看那幾具身體...
    崔向北閱讀 224評論 0 1
  • 我相信 柳蔭 冬天的河干涸了乍构,我相信,春水還將來臨哥遮,那時白帆就是我們心中自由的偶像;風(fēng)中的樹葉凋零了奥帘,我相信,泥土...
    消失在深海best閱讀 281評論 0 0