iOS開發(fā):學(xué)習(xí)Runtime

學(xué)習(xí)iOS開發(fā)劣欢,runtime這個知識點(diǎn)是繞不過去的杉畜,但對于我這種學(xué)習(xí)OC不是太久,寫OC的量不夠多的人來說蹭秋,抽象理解runtime的概念或者是看源代碼有點(diǎn)枯燥扰付,效果也不好,以例子的方法學(xué)習(xí)可能會更好感凤,隨著代碼量的上升悯周,對runtime的理解會越來越深入。
詳細(xì)代碼ARRuntimeDemo,開發(fā)環(huán)境Xcode9.4

Person.h為:

#import <Foundation/Foundation.h>

@interface Person : NSObject
{
    NSString * firstName;
}
@property (nonatomic, assign) int age;

+(void)run;
+(void)study;

-(void)f1;
-(void)f2;

@end

Person.m為:

#import "Person.h"

@implementation Person
{
    NSString *lastname;  
    float weight;
}

-(instancetype)init {
    self = [super init];
    if (self) {
        firstName = @"Andy";
        
    }
    return self;
}

-(void)f1 {
    NSLog(@"執(zhí)行f1");
}

-(void)f2 {
    NSLog(@"執(zhí)行f2");
}

+ (void)run {
    NSLog(@"跑");
}

+ (void)study {
    NSLog(@"學(xué)習(xí)");
}

@end

1 獲取類的所有變量(包括成員變量和屬性變量)

// 1. 獲取所有變量陪竿,包括成員變量和屬性變量
- (IBAction)getAllVar:(UIButton *)sender {
    unsigned int count = 0;
    Ivar *allVariables = class_copyIvarList([Person class], &count);
    
    for (int i=0; i<count; i++) {
        Ivar ivar = allVariables[i];
        const char *Variablename = ivar_getName(ivar);
        const char *VariableType = ivar_getTypeEncoding(ivar);
        
        NSLog(@"Name: %s  Type: %s", Variablename, VariableType);
    }
}

結(jié)果輸出(其中firstName禽翼、lastname屠橄、weight為成員變量,_age為屬性變量):

2018-06-01 17:04:56.275194+0800 ARRuntimeDemo[60670:5448260] Name: firstName  Type: @"NSString"
2018-06-01 17:04:56.276120+0800 ARRuntimeDemo[60670:5448260] Name: lastname  Type: @"NSString"
2018-06-01 17:04:56.276503+0800 ARRuntimeDemo[60670:5448260] Name: weight  Type: f
2018-06-01 17:04:56.276614+0800 ARRuntimeDemo[60670:5448260] Name: _age  Type: i

解釋:

  • Iva闰挡,一個指向objc_ivar結(jié)構(gòu)體指針,包含了變量名锐墙、變量類型等信息
  • 像lastname、weight這種定義在@implementation所謂的私有變量也可獲取
  • 對應(yīng)class_copyIvarList還有一個class_copyPropertyList只能獲得屬性變量的方法

2 獲取所有方法(不包括類方法)

// 2. 獲取所有方法长酗,不包括類方法
- (IBAction)getAllMethod:(UIButton *)sender {
    unsigned int count;
    //獲取方法列表溪北,所有在.m文件顯式實(shí)現(xiàn)的方法都會被找到,包括setter+getter方法夺脾;
    Method *allMethods = class_copyMethodList([Person class], &count);
    for(int i =0;i<count;i++) {
        //Method之拨,為runtime聲明的一個宏,表示對一個方法的描述
        Method md = allMethods[i];
        //獲取SEL:SEL類型,即獲取方法選擇器@selector()
        SEL sel = method_getName(md);
        //得到sel的方法名:以字符串格式獲取sel的name咧叭,也即@selector()中的方法名稱
        const char *methodname = sel_getName(sel);
        
        NSLog(@"(Method:%s)",methodname);
    }
}

結(jié)果輸出:

2018-06-01 16:54:50.433232+0800 ARRuntimeDemo[60482:5418318] (Method:f1)
2018-06-01 16:54:50.433465+0800 ARRuntimeDemo[60482:5418318] (Method:f2)
2018-06-01 16:54:50.433930+0800 ARRuntimeDemo[60482:5418318] (Method:.cxx_destruct)
2018-06-01 16:54:50.434335+0800 ARRuntimeDemo[60482:5418318] (Method:init)
2018-06-01 16:54:50.435163+0800 ARRuntimeDemo[60482:5418318] (Method:height)
2018-06-01 16:54:50.435788+0800 ARRuntimeDemo[60482:5418318] (Method:setHeight:)
2018-06-01 16:54:50.435990+0800 ARRuntimeDemo[60482:5418318] (Method:setAge:)
2018-06-01 16:54:50.436482+0800 ARRuntimeDemo[60482:5418318] (Method:age)

解釋:

  • 獲得了像height,setHeight這種隱藏的setter蚀乔、getter方法
  • Method是一個指向objc_method結(jié)構(gòu)體指針,表示對類中的某個方法的描述菲茬。
  • .cxx_destruct是關(guān)于系統(tǒng)自動內(nèi)存釋放的隱藏方法

3 為類添加新屬性

category只能為類添加新方法吉挣,不能添加新屬性,但通過runtime配合category就可以達(dá)到添加屬性效果婉弹。
首先新建一個類Person的category:
.h文件

//  Person+Category.h

#import "Person.h"

@interface Person (Category)

@property (nonatomic, assign)float height;

@end

.m文件

//  Person+Category.m

#import "Person+Category.h"
#import <objc/runtime.h>

const char *key = "myKey";

@implementation Person (Category)

-(void)setHeight:(float)height {
    NSNumber *num = [NSNumber numberWithFloat:height];
    /*
     objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
     第一個參數(shù)是需要添加屬性的對象睬魂;
     第二個參數(shù)是屬性的key,是C字符串就可以;
     第三個參數(shù)是屬性的值,類型必須為id镀赌,所以此處height先轉(zhuǎn)為NSNumber類型氯哮;
     第四個參數(shù)是使用策略,是一個枚舉值佩脊,類似@property屬性創(chuàng)建時設(shè)置屬性修飾符蛙粘,可從命名看出各枚舉的意義;
     */
    objc_setAssociatedObject(self, key, num, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

-(float)height {
    NSNumber *number = objc_getAssociatedObject(self, key);
    return [number floatValue];
}
@end

然后就能訪問新屬性height了:

- (IBAction)addVar:(UIButton *)sender {
    per = [[Person alloc] init];
    
    per.height = 123;
    NSLog(@"%f", [per height]);
}

此時雖然通過上面的獲取所有變量方法不能獲取height,但通過上面的額獲取所有方法可以獲取heightsetHeight方法了:

2018-06-01 17:14:36.945742+0800 ARRuntimeDemo[60892:5482950] Name: firstName  Type: @"NSString"
2018-06-01 17:14:36.948330+0800 ARRuntimeDemo[60892:5482950] Name: lastname  Type: @"NSString"
2018-06-01 17:14:36.948771+0800 ARRuntimeDemo[60892:5482950] Name: weight  Type: f
2018-06-01 17:14:36.949166+0800 ARRuntimeDemo[60892:5482950] Name: _age  Type: i

2018-06-01 17:15:02.198444+0800 ARRuntimeDemo[60892:5482950] (Method:f1)
2018-06-01 17:15:02.198620+0800 ARRuntimeDemo[60892:5482950] (Method:f2)
2018-06-01 17:15:02.198800+0800 ARRuntimeDemo[60892:5482950] (Method:.cxx_destruct)
2018-06-01 17:15:02.198917+0800 ARRuntimeDemo[60892:5482950] (Method:init)
2018-06-01 17:15:02.199048+0800 ARRuntimeDemo[60892:5482950] (Method:height)
2018-06-01 17:15:02.199150+0800 ARRuntimeDemo[60892:5482950] (Method:setHeight:)
2018-06-01 17:15:02.199239+0800 ARRuntimeDemo[60892:5482950] (Method:setAge:)
2018-06-01 17:15:02.199356+0800 ARRuntimeDemo[60892:5482950] (Method:age)

4 添加新方法

// 4. 添加新方法
- (IBAction)addMethod:(UIButton *)sender {
    /* 動態(tài)添加方法:
     第一個參數(shù)表示Class cls 類型威彰;
     第二個參數(shù)表示待調(diào)用的方法名稱;
     第三個參數(shù)(IMP)myAddingFunction穴肘,IMP一個函數(shù)指針歇盼,這里表示指定具體實(shí)現(xiàn)方法myAddingFunction;
     第四個參數(shù)表方法的參數(shù)评抚,0代表沒有參數(shù)豹缀;
     */
    class_addMethod([per class], @selector(NewMethod), (IMP)myAddingFunction, 0);
    //調(diào)用方法 【如果使用[per NewMethod]調(diào)用方法,在ARC下會報“no visible @interface"錯誤】
    [per performSelector:@selector(NewMethod)];
}
//具體的實(shí)現(xiàn)(方法的內(nèi)部都默認(rèn)包含兩個參數(shù)Class類和SEL方法慨代,被稱為隱式參數(shù)邢笙。)
int myAddingFunction(id self, SEL _cmd){
    NSLog(@"已新增方法:NewMethod");
    return 1;
}

輸出:

2018-06-01 17:31:56.113168+0800 ARRuntimeDemo[61295:5543319] 已新增方法:NewMethod

5 交換兩個方法

// 5. 交換兩個方法
- (IBAction)swapMethod:(UIButton *)sender {
    [Person run];
    [Person study];
    
    Method m1 = class_getClassMethod([Person class], @selector(run));
    Method m2 = class_getClassMethod([Person class], @selector(study));
    
    method_exchangeImplementations(m1, m2);
    
    [Person run];
    [Person study];
}

輸出:

2018-06-01 17:39:00.497375+0800 ARRuntimeDemo[61448:5566239] 跑
2018-06-01 17:39:00.497841+0800 ARRuntimeDemo[61448:5566239] 學(xué)習(xí)
2018-06-01 17:39:00.499255+0800 ARRuntimeDemo[61448:5566239] 學(xué)習(xí)
2018-06-01 17:39:00.499449+0800 ARRuntimeDemo[61448:5566239] 跑

這篇文章我只是做了runtime一些簡單使用,并沒有相關(guān)的使用場景侍匙,算是入門了氮惯,文末參考提到的文章都是不錯,值得以后深入。

參考:
iOS開發(fā) -- Runtime 的幾個小例子
OC最實(shí)用的runtime總結(jié)妇汗,面試帘不、工作你看我就足夠了!
iOS-RunTime杨箭,不再只是聽說
Runtime全方位裝逼指南
Objective-C Runtime

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末寞焙,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子互婿,更是在濱河造成了極大的恐慌捣郊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慈参,死亡現(xiàn)場離奇詭異模她,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)懂牧,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門侈净,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人僧凤,你說我怎么就攤上這事畜侦。” “怎么了躯保?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵旋膳,是天一觀的道長。 經(jīng)常有香客問我途事,道長验懊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任尸变,我火速辦了婚禮义图,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘召烂。我一直安慰自己碱工,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布奏夫。 她就那樣靜靜地躺著怕篷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪酗昼。 梳的紋絲不亂的頭發(fā)上廊谓,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機(jī)與錄音麻削,去河邊找鬼蒸痹。 笑死春弥,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的电抚。 我是一名探鬼主播惕稻,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蝙叛!你這毒婦竟也來了俺祠?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤借帘,失蹤者是張志新(化名)和其女友劉穎蜘渣,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肺然,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蔫缸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了际起。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拾碌。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖街望,靈堂內(nèi)的尸體忽然破棺而出校翔,到底是詐尸還是另有隱情,我是刑警寧澤灾前,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布防症,位于F島的核電站,受9級特大地震影響哎甲,放射性物質(zhì)發(fā)生泄漏蔫敲。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一炭玫、第九天 我趴在偏房一處隱蔽的房頂上張望奈嘿。 院中可真熱鬧,春花似錦础嫡、人聲如沸指么。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至晚唇,卻和暖如春巫财,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哩陕。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工平项, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赫舒,地道東北人。 一個月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓闽瓢,卻偏偏與公主長得像接癌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子扣讼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

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

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時...
    歐辰_OSR閱讀 29,373評論 8 265
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對...
    cosWriter閱讀 11,098評論 1 32
  • 引導(dǎo) 對于從事 iOS 開發(fā)人員來說缺猛,所有的人都會答出「 Runtime 是運(yùn)行時 」,什么情況下用 Runtim...
    Winny_園球閱讀 4,207評論 3 75
  • 在遇見對的人之前椭符,練成金剛鉆石心? 又是一年櫻花開荔燎,還記得去年櫻花開的正旺,我靜待春暖销钝,你如期而至有咨。你如春雨似甘霖...
    Charlotte_yue閱讀 193評論 0 0
  • 根據(jù)研究發(fā)現(xiàn),孩子在五歲到七歲之間蒸健,大腦前額葉會進(jìn)行發(fā)展和重組的一個過程座享,就是因?yàn)檫@些變化才讓元記憶的發(fā)展有了可能...
    瞧那一家子閱讀 535評論 0 0