OC運(yùn)行時(shí)Runtime及相關(guān)使用

Runtime就是運(yùn)行時(shí)动漾,也就是系統(tǒng)在運(yùn)行過程中的一些機(jī)制怎燥。Objective-C就是運(yùn)行時(shí)語言。
對(duì)于C語言來說堤魁,函數(shù)的調(diào)用時(shí)在編譯階段就決定了的喂链。而對(duì)于OC來說,函數(shù)的調(diào)用是動(dòng)態(tài)的妥泉,在編譯的時(shí)候并不能真正決定調(diào)用哪個(gè)函數(shù)椭微。所以,在編譯階段盲链,OC可以調(diào)用任何函數(shù)蝇率,只要函數(shù)有聲明即可,而C則不然刽沾,如果函數(shù)只有聲明而沒有實(shí)現(xiàn)本慕,則會(huì)報(bào)錯(cuò)。

消息機(jī)制

runtime中最重要的就是消息機(jī)制侧漓。OC中任何方法的調(diào)用锅尘,其本質(zhì)都是通過runtime去發(fā)送消息,也就是說布蔗,OC底層是通過runtime去實(shí)現(xiàn)的藤违。

//創(chuàng)建一個(gè)NSObject類型的對(duì)象obj
NSObject *obj = [[NSObject alloc] init];
//為了方便說明浪腐,我們將該行代碼的兩步拆分開來,即
id obj = [NSObject alloc]; //分配內(nèi)存
obj = [obj init]; //初始化

假設(shè)這兩行代碼所在文件為XXX.m顿乒,使用終端打開文件目錄议街,輸入命令 clang -rewrite-objc XXX.m ,運(yùn)行后發(fā)現(xiàn)文件所在目錄下新增了一個(gè)名為“XXX.cpp”的C++文件淆游,打開該文件傍睹,我們可以搜索到上面兩行代碼的底層實(shí)現(xiàn)如下:

id obj = ((NSObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSObject"), sel_registerName("alloc"));
obj = ((id (*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("init"));

去掉括號(hào)中的強(qiáng)轉(zhuǎn)和無用代碼,我們便對(duì)這兩行代碼完成了“瘦身”:

//id obj = [NSObject alloc];
id obj = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));

//obj = [obj init];
obj = objc_msgSend(obj, sel_registerName("init"));

我們發(fā)現(xiàn)每個(gè)方法的底層都是通過發(fā)送消息——objc_msgSend(); 犹菱。
事實(shí)上拾稳,我們確實(shí)也可以在XXX.m文件中用這兩行代碼代替原有的兩行代碼。
由于XCode6之后腊脱,蘋果不再鼓勵(lì)開發(fā)人員使用runtime編寫代碼访得,所以直接使用會(huì)報(bào)錯(cuò)。我們可以找到工程文件->BuildSetting->搜索msg->Enable Strict Checking of objc_msgSend Calls陕凹,將默認(rèn)的YES改為No即可悍抑。

設(shè)置允許使用runtime.png

然后在我們需要用到runtime的文件中導(dǎo)入頭文件#import<objc/message.h>。
上面“瘦身”后的代碼仍然過于復(fù)雜杜耙,實(shí)際用起來很不方便搜骡。下面我們對(duì)函數(shù)objc_msgSend()作一下說明,加入上層的東西佑女,進(jìn)一步精簡代碼记靡,方便開發(fā)中使用。

//id 消息由誰發(fā)送
//SEL 發(fā)送什么消息
objc_msgSend(<#id self#>, <#SEL op, ...#>)

所以团驱,我們對(duì)原有的代碼簡化如下:

//id obj = [NSObject alloc];
id obj = objc_msgSend([NSObject class], @selector(alloc));
//obj = [obj init];
obj = objc_msgSend(obj, @selector(init));

涉及到需要參數(shù)的函數(shù)摸吠,用runtime實(shí)現(xiàn)的方式是類似的。

在Dog類中有一個(gè)函數(shù)
- (void)bark:(int)times
{
    NSLog(@"叫了%d聲",times);
}

其他文件中嚎花,可以作如下調(diào)用:

Dog *littleDog = [[Dog alloc] init];
objc_msgSend(littleDog, @selector(bark:), 3); 

需要指出的是寸痢,使用runtime可以調(diào)用一個(gè)類的私有方法,及時(shí)Dog.h文件中沒有聲明bark:方法紊选,依然可以使用runtime調(diào)用啼止。

使用Runtime交換方法

場(chǎng)景:想要在原有項(xiàng)目代碼中,對(duì)系統(tǒng)類UIImage中的方法-imageNamed:添加“判斷圖片是否成功加載”的功能兵罢。由于原有代碼中很可能多次用到過UIImage的這個(gè)方法族壳,所以自定義方法或者重寫+imageNamed:等常規(guī)思路去實(shí)現(xiàn)會(huì)很難或者很麻煩。
想要修改系統(tǒng)的方法實(shí)現(xiàn)趣些,給系統(tǒng)方法添加新的功能仿荆,我們可以利用runtime進(jìn)行方法交換。
以該場(chǎng)景舉例,創(chuàng)建一個(gè)UIImage的分類拢操,在該分類中完成如下操作即可锦亦。

#import "UIImage+LFImage.h"
#import <objc/message.h>
@implementation UIImage (LFImage)

//+load方法在將類加載進(jìn)內(nèi)存的時(shí)候調(diào)用,并且只會(huì)調(diào)用一次
//所以我們?cè)谠摲椒ㄖ羞M(jìn)行兩個(gè)方法的交換
+(void)load
{
    //獲取+imageNamed:方法
    Method method1 = class_getClassMethod(self, @selector(imageNamed:));
    //獲取+lf_imageNamed:方法
    Method method2 = class_getClassMethod(self, @selector(lf_imageNamed:));
    //交換方法
    method_exchangeImplementations(method1, method2);
    //完成這步之后令境,調(diào)用+iamgeNamed:實(shí)際調(diào)用的是+lf_imageNamed:杠园,反之亦然
}

+(UIImage *)lf_imageNamed:(NSString *)name
{
    //加載圖片
    UIImage *image = [UIImage lf_imageNamed:name];
    //由于方法已交換,所以此時(shí)如果寫imageNamed: 舔庶,會(huì)造成死循環(huán)
    
    //判斷是否加載成功
    if (image)
        NSLog(@"加載成功");
    else
        NSLog(@"加載失敗");
    
    return image;
}

@end

這樣抛蚁,我們就完成了 +imageNamed: 和 lf_imageNamed: 的交換,項(xiàng)目中所有 +imageNamed: 的調(diào)用惕橙,其實(shí)都是調(diào)用的lf_imageNamed: 方法瞧甩。

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

動(dòng)態(tài)添加方法在實(shí)際開發(fā)中并不太常用,此處做簡要說明弥鹦。
創(chuàng)建一個(gè)對(duì)象dog肚逸,并利用performSelector:調(diào)用Dog類中既沒有聲明有咩有實(shí)現(xiàn)的方法run。

Dog *dog = [[Dog alloc] init];
[dog performSelector:@selector(run)];

需要注意的是彬坏,由于Dog類中沒有聲明方法run朦促,所以直接[Dog run]編譯器會(huì)報(bào)錯(cuò)。
由于沒有實(shí)現(xiàn)方法run栓始,所以在程序運(yùn)行的時(shí)候仍然會(huì)報(bào)錯(cuò)务冕。我們需要在Dog類中做如下處理。

#import "Dog.h"
#import <objc/message.h>
@implementation Dog

//當(dāng)一個(gè)對(duì)象調(diào)用了未實(shí)現(xiàn)的方法時(shí)幻赚,會(huì)調(diào)用該方法
//該方法用于動(dòng)態(tài)添加方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    NSLog(@"%@", NSStringFromSelector(sel));
    
    //判斷是否為run
    if (sel == NSSelectorFromString(@"run")) {
        //__unsafe_unretained Class cls : 給哪個(gè)類添加方法
        //SEL name : 什么方法
        //IMP imp : 方法實(shí)現(xiàn)--方法名
        //const char *types : 方法類型
        class_addMethod(self, sel, (IMP)rrrun, "v@:");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

//當(dāng)一個(gè)類調(diào)用了未實(shí)現(xiàn)的方法
//+ (BOOL)resolveClassMethod:(SEL)sel
//{
//    
//}

//此處不會(huì)生成方法列表
void rrrun(id self, SEL _cmd) {
    NSLog(@"跑啊跑");
}
@end

動(dòng)態(tài)添加屬性

利用runtime動(dòng)態(tài)添加屬性禀忆,在實(shí)際開發(fā)中相對(duì)是比較常用的。自定義的類坯屿,我們可以自由設(shè)置屬性,所以此處主要作用于系統(tǒng)的類巍扛。動(dòng)態(tài)添加屬性的本質(zhì)领跛,就是讓某個(gè)屬性與對(duì)象產(chǎn)生一個(gè)關(guān)聯(lián)。
例如撤奸,我們對(duì)NSObject添加一個(gè)name屬性吠昭。

NSObject *obj = [[NSObject alloc] init];
obj.name = @"對(duì)象"; //由于NSObject類沒有name屬性,顯然此處會(huì)報(bào)錯(cuò)

這里給NSObject添加一個(gè)分類NSObject+Name胧瓜,然后在分類中處理如下矢棚。

#import <Foundation/Foundation.h>

@interface NSObject (Name)

@property NSString *name;
//由于在分類中,@property只會(huì)生成getter府喳、setter的聲明蒲肋,所以沒有必要對(duì)屬性進(jìn)行修飾

@end
#import "NSObject+Name.h"
#import <objc/message.h>

@implementation NSObject (Name)

- (void)setName:(NSString *)name
{
    //讓name屬性與當(dāng)前對(duì)象產(chǎn)生關(guān)聯(lián)
    //object 需要添加屬性的對(duì)象
    //key 屬性名稱
    //value 值
    //policy 保存策略
    /*
    OBJC_ASSOCIATION_ASSIGN = 0,
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,
    OBJC_ASSOCIATION_RETAIN = 01401,
    OBJC_ASSOCIATION_COPY = 01403
     */
    objc_setAssociatedObject(self, @"name", @"對(duì)象", OBJC_ASSOCIATION_COPY);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, @"name");
}
@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子兜粘,更是在濱河造成了極大的恐慌申窘,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評(píng)論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孔轴,死亡現(xiàn)場(chǎng)離奇詭異剃法,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)路鹰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門贷洲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晋柱,你說我怎么就攤上這事优构。” “怎么了趣斤?”我有些...
    開封第一講書人閱讀 164,057評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵俩块,是天一觀的道長。 經(jīng)常有香客問我浓领,道長玉凯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,509評(píng)論 1 293
  • 正文 為了忘掉前任联贩,我火速辦了婚禮漫仆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘泪幌。我一直安慰自己盲厌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,562評(píng)論 6 392
  • 文/花漫 我一把揭開白布祸泪。 她就那樣靜靜地躺著吗浩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪没隘。 梳的紋絲不亂的頭發(fā)上懂扼,一...
    開封第一講書人閱讀 51,443評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音右蒲,去河邊找鬼阀湿。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瑰妄,可吹牛的內(nèi)容都是我干的陷嘴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼间坐,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼灾挨!你這毒婦竟也來了邑退?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,129評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤涨醋,失蹤者是張志新(化名)和其女友劉穎瓜饥,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體浴骂,經(jīng)...
    沈念sama閱讀 45,561評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡乓土,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,779評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了溯警。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趣苏。...
    茶點(diǎn)故事閱讀 39,902評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖梯轻,靈堂內(nèi)的尸體忽然破棺而出食磕,到底是詐尸還是另有隱情,我是刑警寧澤喳挑,帶...
    沈念sama閱讀 35,621評(píng)論 5 345
  • 正文 年R本政府宣布彬伦,位于F島的核電站,受9級(jí)特大地震影響伊诵,放射性物質(zhì)發(fā)生泄漏单绑。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,220評(píng)論 3 328
  • 文/蒙蒙 一曹宴、第九天 我趴在偏房一處隱蔽的房頂上張望搂橙。 院中可真熱鬧,春花似錦笛坦、人聲如沸区转。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽废离。三九已至,卻和暖如春礁芦,著一層夾襖步出監(jiān)牢的瞬間蜻韭,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評(píng)論 1 269
  • 我被黑心中介騙來泰國打工宴偿, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留湘捎,地道東北人诀豁。 一個(gè)月前我還...
    沈念sama閱讀 48,025評(píng)論 2 370
  • 正文 我出身青樓窄刘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親舷胜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子娩践,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,843評(píng)論 2 354

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

  • 對(duì)于從事 iOS 開發(fā)人員來說活翩,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,721評(píng)論 7 64
  • 轉(zhuǎn)自:http://blog.csdn.net/liangliang103377/article/details/...
    reallychao閱讀 2,401評(píng)論 1 20
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,556評(píng)論 33 466
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,715評(píng)論 0 9
  • 昨晚翻伺,真是一夜夢(mèng)幻連篇起材泄,扶搖直上九百米。長夢(mèng)變幻如演戲吨岭,驚魂晃動(dòng)似無依拉宗。 現(xiàn)在亂了次序。記錄不分先后辣辫。 第一旦事,村...
    一元真人閱讀 196評(píng)論 0 4