runTime初識(shí)

這里就不說runtime有多牛逼了。直接進(jìn)入正題鸟蟹。

首先Objective-C是面向?qū)ο蟮模?strong>任何類的定義都是對象建钥。類和類的實(shí)例(對象)沒有任何本質(zhì)上的區(qū)別锦针。任何對象都有isa指針。


Class 是一個(gè) objc_class 結(jié)構(gòu)類型的指針

要理解runtime就要知道這些參數(shù)的意思:

isa:

isa是一個(gè)Class類型的指針悉盆,每個(gè)被實(shí)例化的對象都有一個(gè)isa指針焕盟,指向相對應(yīng)的類脚翘,然后這個(gè)類也有一個(gè)isa指針来农,指向meteClass(元類)崇堰。
可能很多人理解不了元類的概念海诲,我開始我對元類這個(gè)東西特幔,那時(shí)候還和父類聯(lián)系在一起,也是想了很久才想通薄风。

這里說一下類和對象的關(guān)系(幫助理解元類概念):

很多人知道每一個(gè)對象本質(zhì)上都是一個(gè)類的實(shí)例村刨。其中類定義了成員變量和成員方法的列表。對象通過對象的isa指針指向類打洼。
但其實(shí)每一個(gè)類本質(zhì)上都是一個(gè)對象募疮,類其實(shí)是元類的實(shí)例。元類定義了類方法的列表他嚷。類通過類的isa指針指向元類筋蓖。當(dāng)類方法被調(diào)用時(shí),先會(huì)從本身查找類方法的實(shí)現(xiàn)蚣抗,如果沒有翰铡,元類會(huì)向他父類查找該方法锭魔。同時(shí)注意的是:元類也是類路呜,它也是對象拣宰。元類也有isa指針,它的isa指針最終指向的是一個(gè)根元類(root meteClass).根元類的isa指針指向本身,這樣形成了一個(gè)封閉的內(nèi)循環(huán)膛堤。
super_class:父類肥荔,如果該類已經(jīng)是最頂層的根類,那么它為NULL燕耿。

version:類的版本信息,默認(rèn)為0

info:供運(yùn)行期使用的一些位標(biāo)識(shí)姜胖。

instance_size:該類的實(shí)例變量大小

ivars:成員變量的數(shù)組

runtime的應(yīng)用:
1.動(dòng)態(tài)創(chuàng)建一個(gè)類(比如KVO的底層實(shí)現(xiàn))
2.動(dòng)態(tài)地為某個(gè)類添加屬性\方法, 修改屬性值\方法
3.遍歷一個(gè)類的所有成員變量(屬性)\所有方法
實(shí)質(zhì)上右莱,以上的是通過相關(guān)方法來獲取對象或者類的isa指針來實(shí)現(xiàn)的慢蜓。

這里介紹下runtime中經(jīng)常出現(xiàn)的一些字眼(其實(shí)翻譯下就知道了):

  1. 增加

增加函數(shù):class_addMethod

增加實(shí)例變量:class_addIvar

增加屬性:@dynamic標(biāo)簽晨抡,或者class_addMethod则剃,因?yàn)閷傩云鋵?shí)就是由getter和setter函數(shù)組成

增加Protocol:class_addProtocol (說實(shí)話我真不知道動(dòng)態(tài)增加一個(gè)protocol有什么用,-_-!!)

  1. 獲取

獲取函數(shù)列表及每個(gè)函數(shù)的信息(函數(shù)指針棍现、函數(shù)名等等):class_getClassMethod method_getName ...

獲取屬性列表及每個(gè)屬性的信息:class_copyPropertyList property_getName

獲取類本身的信息,如類名等:class_getName class_getInstanceSize

獲取變量列表及變量信息:class_copyIvarList

獲取變量的值

  1. 替換

將實(shí)例替換成另一個(gè)類:object_setClass

替換類方法的定義:class_replaceMethod

4.其他常用方法:

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

設(shè)置一個(gè)方法的實(shí)現(xiàn):method_setImplementation.

之前比較空的時(shí)候就簡單的整理了一下轴咱,這邊以類方法形式實(shí)現(xiàn) 使用runtime記得導(dǎo)入<objc/message.h>,<objc/runtime.h>朴肺。
/**
 *  獲取對象的所有屬性以及相對應(yīng)的值
 *
 *  @param className 一個(gè)實(shí)例化的對象
 *
 *  @return 字典(屬性-值)
 */
+ (NSDictionary *)getAllPropertyNamesAndValuesWithClassName:(id)className {
    NSMutableDictionary *dict = [NSMutableDictionary dictionary];

    unsigned int count;
    objc_property_t *properties = class_copyPropertyList([className class], &count);

    for (int i = 0; i < count; i++) {
        objc_property_t property = properties[i];
        const char *name = property_getName(property);

        // 得到屬性名
        NSString *propertyName = [NSString stringWithUTF8String:name];

        // 獲取屬性值
        id propertyValue = [className valueForKey:propertyName];

        if (propertyValue && propertyValue != nil) {
            [dict setObject:propertyValue forKey:propertyName];
        }
    }
    // 記得釋放
    free(properties);
    return dict;
}
/**
 *  獲取一個(gè)類(系統(tǒng)類也可以,包括未在.h中沒有聲名的屬性)的所有私有屬性
 *
 *  @param className 類名(注意:不能傳對象)
 */
+ (void)getAttributeWithClassName:(id)className {

    u_int count;
    objc_property_t *properties = class_copyPropertyList(className, &count);
    for (int i = 0; i < count; i++) {
        const char *propertyName = property_getName(properties[i]);
        NSString *strName = [NSString stringWithCString:propertyName encoding:NSUTF8StringEncoding];
        NSLog(@"%@", strName);
    }
}
/**
 *  獲取一個(gè)類(系統(tǒng)類也可以,包括未在.h中沒有聲名的屬性和成員變量)的所有私有屬性和成員變量
 *
 *  @param className 類名(注意:不能傳對象)
 *
 *  @return 一個(gè)數(shù)組
 */
+ (NSArray *)getAllMemberVariables:(id)className {
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList(className, &count);

    NSMutableArray *results = [[NSMutableArray alloc] init];
    for (NSUInteger i = 0; i < count; ++i) {
        Ivar variable = ivars[i];

        const char *name = ivar_getName(variable);
        NSString *varName = [NSString stringWithUTF8String:name];

        [results addObject:varName];
    }

    return results;
}
/**
 *  獲取某個(gè)類(包括系統(tǒng)類)的所有方法,包括系統(tǒng)類以及其中沒有在.h聲明的方法
 *
 *  @param className 類名(注意:不能傳對象)
 */
+ (void)getAllMethodsWithClassName:(id)className {
    unsigned int outCount = 0;
    Method *methods = class_copyMethodList(className, &outCount);

    for (int i = 0; i < outCount; ++i) {
        Method method = methods[i];

        // 獲取方法名稱鞍盗,但是類型是一個(gè)SEL選擇器類型
        SEL methodSEL = method_getName(method);
        // 需要獲取C字符串
        const char *name = sel_getName(methodSEL);
        // 將方法名轉(zhuǎn)換成OC字符串
        NSString *methodName = [NSString stringWithUTF8String:name];

        // 獲取方法的參數(shù)列表
        int arguments = method_getNumberOfArguments(method);
        NSLog(@"方法名:%@, 參數(shù)個(gè)數(shù):%d", methodName, arguments);
    }

    // 記得釋放
    free(methods);
}

方法交換

/**
 *  方法交換也適用系統(tǒng)API
 *
 *  @param oldClassName 調(diào)用舊方法的類名
 *  @param oldMethod    舊方法
 *  @param newClassName 調(diào)用新方法的類名
 *  @param newMethod    新方法
 */
+ (void)methodSwizzlingAboutOldClassName:(id)oldClassName WithOldMethod:(SEL)oldMethod WithNewClassName:(id)newClassName WithNewMethod:(SEL)newMethod {

    Method old_Method = class_getInstanceMethod(oldClassName, oldMethod);
    Method new_Method = class_getInstanceMethod(newClassName, newMethod);
    method_exchangeImplementations(old_Method, new_Method);
}
-(void)encodeWithCoder:(NSCoder *)aCoder{
    //利用runTime進(jìn)行歸檔
    //利用個(gè)數(shù)取出對應(yīng)的屬性(kvc)屬性的名稱
    //通過runtime來取出屬性
    unsigned int count=0;
    Ivar *ivars=class_copyIvarList([self class], &count);
    for(int i=0;i<count;i++){
        Ivar ivar=ivars[i];
        const char *name=ivar_getName(ivar);
        NSString *key=[NSString stringWithUTF8String:name];
        [aCoder encodeObject:[self valueForKey:key] forKey:key];
    }
    free(ivars);
}
-(instancetype)initWithCoder:(NSCoder *)aDecoder{
    if(self=[super init]){
        
        unsigned int count=0;
        Ivar *ivars=class_copyIvarList([self class], &count);
        for(int i=0;i<count;i++){
            Ivar ivar=ivars[i];
            const char *name=ivar_getName(ivar);
            NSString *key=[NSString stringWithUTF8String:name];
            //            id value=[self valueForKey:key];
            id value=[aDecoder decodeObjectForKey:key];
        }
        free(ivars);
    }
    return self;
}

消息轉(zhuǎn)發(fā)機(jī)制

在調(diào)用某一個(gè)方法時(shí),系統(tǒng)會(huì)查看這個(gè)對象能否接收這個(gè)消息(查看這個(gè)類有沒有這個(gè)方法敷存,或者有沒有實(shí)現(xiàn)這個(gè)方法堪伍。),如果不能并且只在不能的情況下涮俄,就會(huì)調(diào)用下面這幾個(gè)方法彻亲,給你“補(bǔ)救”的機(jī)會(huì)吮廉,你可以先理解為幾套防止程序crash的備選方案茧痕,我們就是利用這幾個(gè)方案進(jìn)行消息轉(zhuǎn)發(fā)踪旷,注意一點(diǎn),前一套方案實(shí)現(xiàn)后一套方法就不會(huì)執(zhí)行舀患。如果這幾套方案你都沒有做處理气破,那么程序就會(huì)報(bào)錯(cuò)crash。
方案一:
+ (BOOL)resolveInstanceMethod:(SEL)sel
+ (BOOL)resolveClassMethod:(SEL)sel
方案二:
-(id)forwardingTargetForSelector:(SEL)aSelector
方案三:
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)anInvocation;

#import <UIKit/UIKit.h>

@interface RunTimeViewController : UIViewController
//屬性(與外界交流的)
@property (nonatomic, strong) NSString *name;
@property (nonatomic, assign) NSInteger age;
@property (nonatomic, strong) NSString *sex;

-(void)test1;
@end
#import "NSString+testAboutRuntime.h"
#import "RunTimeManage.h"
#import "RunTimeViewController.h"
#import <objc/message.h>
#import <objc/runtime.h>
#import "MYWebViewController.h"
#import <BaiduMapAPI_Search/BMKPoiSearchOption.h>
@interface RunTimeViewController () {
    //成員變量
    NSString *weight;
}

@property (nonatomic, strong) NSString *phoneNumber;
-(void)test3:(NSString *)str;
@end

@implementation RunTimeViewController
- (id)init {
    if (self = [super init]) {
        [RunTimeManage methodSwizzlingAboutOldClassName:[self class] WithOldMethod:@selector(viewDidLoad) WithNewClassName:[self class] WithNewMethod:@selector(viewDidLoadByMjj)];
    }
    return self;
}
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
}
- (void)viewDidLoadByMjj {
    [self.view setBackgroundColor:[UIColor orangeColor]];
    [super viewDidLoad];
    // Do any additional setup after loading the view from its nib.
    [RunTimeManage methodSwizzlingAboutOldClassName:[self class] WithOldMethod:@selector(test1) WithNewClassName:[self class] WithNewMethod:@selector(test2)];
    
    
    [self test1];
    NSString *str = [[NSString alloc] init];
    str.name = @"zhangsan";
    //    str.AttributeName=@"1111";
    NSLog(@"%@", str.name);
    //    NSLog(@"%@",str.AttributeName);
    self.name = @"mjj";
    self.sex = @"男";
    self.age = 22;
    
    [self test3:self.name];
    
    
    [RunTimeManage getAllMemberVariables:[RunTimeViewController class]];
    
    [RunTimeManage getAttributeWithClassName:[RunTimeViewController class]];
    [self setValue:@"女" forKey:@"sex"];
    
    [RunTimeManage getAllMethodsWithClassName:[NSString class]];
    
    [RunTimeManage getAllMethodsWithClassName:[BMKBasePoiSearchOption class]];
    [RunTimeManage getAllPropertyNamesAndValuesWithClassName:self];
    NSLog(@"%@", [RunTimeManage getAllPropertyNamesAndValuesWithClassName:self]);
    NSLog(@"%@", [[RunTimeManage getAllPropertyNamesAndValuesWithClassName:self] valueForKey:@"sex"]);
}
- (void)test1 {
    
    NSLog(@"111111");
}
- (void)test2 {
    
    NSLog(@"222222");
}
void functionForMethod1(id self,SEL _cmd){
    
    NSLog(@"動(dòng)態(tài)添加方法SUCCESS");
}
//----------先動(dòng)態(tài)決議碳锈,動(dòng)態(tài)決議返回NO,進(jìn)入消息轉(zhuǎn)發(fā)——————————————————
//動(dòng)態(tài)實(shí)現(xiàn)某個(gè)方法(動(dòng)態(tài)決議)
+(BOOL)resolveInstanceMethod:(SEL)sel{
    
    NSString *selectorString=NSStringFromSelector(sel);
    if([selectorString isEqualToString:@"test3:"]){
        
        class_addMethod(self, sel, (IMP)functionForMethod1, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
//消息轉(zhuǎn)發(fā)
- (id)forwardingTargetForSelector:(SEL)aSelector{
    NSString *selectorString=NSStringFromSelector(aSelector);
   //先執(zhí)行 resolveInstanceMethod强重,如果找到方法則不執(zhí)行該方法
    if([selectorString isEqualToString:@"test3:"]){
        //返回一個(gè)對象间景,如果該對象實(shí)現(xiàn)了該方法則程序不會(huì)crash倘要。
        return [[MyWebViewController alloc]init];
    }
    return nil;
}
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

具體看我的代碼吧力惯,應(yīng)該還是可以看懂的父晶,其中可能會(huì)有比較多的代碼冗余,因?yàn)檎{(diào)試時(shí)候牽扯比較多尝苇。歡迎交流埠胖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末直撤,一起剝皮案震驚了整個(gè)濱河市谋竖,隨后出現(xiàn)的幾起案子承匣,更是在濱河造成了極大的恐慌韧骗,老刑警劉巖零聚,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隶症,死亡現(xiàn)場離奇詭異,居然都是意外死亡览徒,警方通過查閱死者的電腦和手機(jī)习蓬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進(jìn)店門躲叼,熙熙樓的掌柜王于貴愁眉苦臉地迎上來企巢,“玉大人,你說我怎么就攤上這事或听∷裥觯” “怎么了?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵足丢,是天一觀的道長斩跌。 經(jīng)常有香客問我耀鸦,道長啸澡,這世上最難降的妖魔是什么萝快? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮吏口,結(jié)果婚禮上冰更,老公的妹妹穿的比我還像新娘。我一直安慰自己舟铜,他們只是感情好奠衔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布归斤。 她就那樣靜靜地躺著,像睡著了一般她我。 火紅的嫁衣襯著肌膚如雪迫横。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天恨狈,我揣著相機(jī)與錄音拴事,去河邊找鬼圣蝎。 笑死,一個(gè)胖子當(dāng)著我的面吹牛牲证,可吹牛的內(nèi)容都是我干的坦袍。 我是一名探鬼主播,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蛮放,長吁一口氣:“原來是場噩夢啊……” “哼奠宜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起娩嚼,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤岳悟,失蹤者是張志新(化名)和其女友劉穎泼差,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體春瞬,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡宽气,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年潜沦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片涝影。...
    茶點(diǎn)故事閱讀 39,722評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡燃逻,死狀恐怖臂痕,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情姆怪,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布俺附,位于F島的核電站溪掀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蛮浑。R本人自食惡果不足惜只嚣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一册舞、第九天 我趴在偏房一處隱蔽的房頂上張望障般。 院中可真熱鬧,春花似錦藐石、人聲如沸定拟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽延窜。三九已至,卻和暖如春荠藤,著一層夾襖步出監(jiān)牢的瞬間获高,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工牡彻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人缎除。 一個(gè)月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓器罐,卻偏偏與公主長得像渐行,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子肴沫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,614評論 2 353

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉颤芬,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,709評論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,554評論 33 466
  • 我們常常會(huì)聽說 Objective-C 是一門動(dòng)態(tài)語言站蝠,那么這個(gè)「動(dòng)態(tài)」表現(xiàn)在哪呢菱魔?我想最主要的表現(xiàn)就是 Obje...
    Ethan_Struggle閱讀 2,192評論 0 7
  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡介 Runt...
    樂樂的簡書閱讀 2,135評論 0 9
  • Runtime 運(yùn)行時(shí)澜倦,是一套底層的 C 語言 API拔疚,其為 iOS 內(nèi)部的核心之一,我們平時(shí)編寫的 OC 代碼稚失,...
    剎那_芳華閱讀 302評論 1 0