ios runtime總結(jié)(一)

runtime是運行時機制粗梭,Objective-C是面向運行時的語言,就是說它會盡可能的把編譯和鏈接時要執(zhí)行的邏輯延遲到運行時级零。這就給了我們很大的靈活性断医。可以按照需要把消息重定向給合適的對象,甚至可以交換方法的實現(xiàn)等等孩锡。

下面從以下幾個方面來探索

1酷宵、消息機制
2、方法調(diào)用流程
3躬窜、使用runtime交換方法實現(xiàn)
4浇垦、動態(tài)添加方法
5、動態(tài)添加屬性

1荣挨、消息機制

我們先新建一個命令行項目

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        id objc = [NSObject alloc];
        objc = [objc init];
    }
    return 0;
}

可以使用命令 clang -rewrite-objc- main.m 將其編譯為C++文件
通過查看該文件男韧,我們可以發(fā)現(xiàn),編譯器將其對象創(chuàng)建的兩行代碼編譯為

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

對其不必要的代碼進行刪除后可得到

 objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));
 objc = objc_msgSend(objc, sel_registerName("init"));

從這兩行代碼可明顯看出默垄,Objective-C經(jīng)過編譯后是使用消息機制來調(diào)用方法的此虑。所以我們可以用發(fā)送消息的方式來調(diào)用方法

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

那么我們開發(fā)中什么時候需要用到消息機制呢?當我們想調(diào)用私有方法時口锭,可以用runtime朦前。
下面來看一下帶參數(shù)的方法怎么用runtime調(diào)用

objc_msgSend(<#id  _Nullable self#>, <#SEL  _Nonnull op, ...#>)

從代碼提示可以發(fā)現(xiàn)第三個參數(shù)的...代表可變參數(shù),意味著可以傳入任意個數(shù)的參數(shù)鹃操。

person *p = objc_msgSend([person class], @selector(alloc));
p = objc_msgSend(p, @selector(init));
objc_msgSend(p, @selector(eat:),@"蘋果");

我定義了一個person類韭寸,并且未在.h文件中暴露方法聲明,從運行結(jié)果可以看出荆隘,使用runtime可以實現(xiàn)對私有方法的調(diào)用恩伺。

2、方法調(diào)用流程椰拒。
方法分為對象方法和類方法晶渠。對象方法保存在方法列表中,類方法保存在元類的方法列表中燃观。
1)褒脯、通過isa去對應(yīng)類中查找。
2)缆毁、注冊方法編號
3)番川、根據(jù)方法編號去查找對應(yīng)方法。
4)积锅、找到的只是最終函數(shù)實現(xiàn)地址爽彤,根據(jù)地址去方法區(qū)調(diào)用方法 。

3缚陷、使用runtime交換方法實現(xiàn)
下面我們來看這樣一個場景适篙,當用UIImage加載圖片時,如果需要判斷圖片是否加載成功箫爷,就需要增加一個判斷嚷节,代碼如下:

UIImage *img = [UIImage imageNamed:@"1.png"];
    if (img == nil) {
        NSLog(@"圖片加載失敗");
    }else{
        NSLog(@"圖片加載成功");
    }

若需要多次判斷聂儒,就需要每次都進行判斷,無疑會增加代碼的冗余度硫痰。此時可能會想到給UIImage類增加一個分類衩婚,重寫imageNamed方法,在重寫的方法中進行判斷效斑,每次需要判斷時直接調(diào)用分類重寫的方法非春,但是如果是之前代碼中已經(jīng)調(diào)用了很多次原來的方法,再進行修改就會帶來很大的工作量缓屠,此時我們可以想到runtime奇昙,因為它將方法的調(diào)用推遲到了運行時,我們可以在編譯時對自己重寫的方法和類原有的方法進行調(diào)換敌完,以達到上述目的储耐。
進行方法調(diào)換需要在類加載進內(nèi)存的時候交換,并且只能交換一次滨溉,所以可以在分類的+(void)load方法中交換什湘。主要代碼如下:

+ (void)load
{
    Method imagenamed = class_getClassMethod(self, @selector(imageNamed:));
    Method imageWithName = class_getClassMethod(self, @selector(imageWithName:));
    method_exchangeImplementations(imagenamed, imageWithName);
}
+(UIImage *)imageWithName:(NSString *)name
{
    UIImage *image = [UIImage imageWithName:name];
    if (image) {
        NSLog(@"加載成功");
    }else{
        NSLog(@"加載失敗");
    }
    return image;
}

4、動態(tài)添加方法
為什么要動態(tài)添加方法晦攒?OC都是懶加載機制闽撤,只要一個方法實現(xiàn)了,就會馬上添加到方法列表中勤家。

     person *p = [[person alloc]init];
    [p performSelector:@selector(eat)];
    [p performSelector:@selector(run:) withObject:@"操場"];


@implementation person

void aaa(){
    NSLog(@"aaaa");
}
void run(id self,SEL _cmd,NSString *arg){
    NSLog(@"在%@跑",arg);
}


+(BOOL)resolveInstanceMethod:(SEL)sel
{
    //不帶參數(shù)
    if (sel == NSSelectorFromString(@"eat")) {
        class_addMethod(self,sel,(IMP)aaa,"v@:");
        return YES;
    }
    //帶參數(shù)
    if (sel == NSSelectorFromString(@"run:")) {
        class_addMethod(self, sel, (IMP)run, "v@:");
        return  YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

@end

如上腹尖,當調(diào)用一個對象未實現(xiàn)的方法時柳恐,會調(diào)用該方法內(nèi)部的+(BOOL)resolveInstanceMethod:(SEL)sel方法伐脖,在這個方法內(nèi),我們就可對該對象動態(tài)添加方法乐设。
下面對class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
方法中的參數(shù)進行解釋:
cls:給哪個類添加方法
SEL:添加哪個方法
IMP:方法的實現(xiàn)(函數(shù)入口讼庇,即函數(shù)名)
type:方法類型

5、動態(tài)添加屬性
什么時候需要動態(tài)添加屬性近尚?
本質(zhì):讓某個屬性與對象產(chǎn)生關(guān)聯(lián)蠕啄。
場景:讓NSObject保存string
方法一、給NSObject添加分類

NSObject+cate1.h
@interface NSObject (cate1)

//@property分類: 只會生成get戈锻,set方法聲明歼跟,不會產(chǎn)生實現(xiàn)。也不會產(chǎn)生下劃線成員屬性
@property NSString *name;

@end

NSObject+cate1.m

@implementation NSObject (cate1)

static NSString *_name;

- (void)setName:(NSString *)name
{
    _name = name;
}

-(NSString *)name{
    return _name;
}

@end

弊端:使用全局靜態(tài)變量格遭,當這個類銷毀時哈街,靜態(tài)變量可能還存在

方法二:runtime

objc_setAssociatedObject(<#id  _Nonnull object#>, <#const void * _Nonnull key#>, <#id  _Nullable value#>, <#objc_AssociationPolicy policy#>);

object:給哪個對象添加屬性
key:屬性名稱
value:屬性值
policy:保存策略

.h
@interface NSObject (cate1)

//@property分類: 只會生成get,set方法聲明拒迅,不會產(chǎn)生實現(xiàn)骚秦。也不會產(chǎn)生下劃線成員屬性
@property NSString *name;

@end

.m
@implementation NSObject (cate1)


- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

-(NSString *)name{
    return  objc_getAssociatedObject(self, @"name");
}

@end
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末她倘,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子作箍,更是在濱河造成了極大的恐慌硬梁,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件胞得,死亡現(xiàn)場離奇詭異荧止,居然都是意外死亡,警方通過查閱死者的電腦和手機阶剑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門罩息,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人个扰,你說我怎么就攤上這事瓷炮。” “怎么了递宅?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵娘香,是天一觀的道長。 經(jīng)常有香客問我办龄,道長烘绽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任俐填,我火速辦了婚禮安接,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘英融。我一直安慰自己盏檐,他們只是感情好,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布驶悟。 她就那樣靜靜地躺著胡野,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痕鳍。 梳的紋絲不亂的頭發(fā)上硫豆,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天,我揣著相機與錄音笼呆,去河邊找鬼熊响。 笑死,一個胖子當著我的面吹牛诗赌,可吹牛的內(nèi)容都是我干的汗茄。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼境肾,長吁一口氣:“原來是場噩夢啊……” “哼剔难!你這毒婦竟也來了胆屿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤偶宫,失蹤者是張志新(化名)和其女友劉穎非迹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纯趋,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡憎兽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了吵冒。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片纯命。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖痹栖,靈堂內(nèi)的尸體忽然破棺而出亿汞,到底是詐尸還是另有隱情,我是刑警寧澤揪阿,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布疗我,位于F島的核電站,受9級特大地震影響南捂,放射性物質(zhì)發(fā)生泄漏吴裤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一溺健、第九天 我趴在偏房一處隱蔽的房頂上張望麦牺。 院中可真熱鬧,春花似錦鞭缭、人聲如沸剖膳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽潮秘。三九已至琼开,卻和暖如春易结,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背柜候。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工搞动, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人渣刷。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓鹦肿,卻偏偏與公主長得像,于是被迫代替她去往敵國和親辅柴。 傳聞我的和親對象是個殘疾皇子箩溃,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354