runtime詳解

runtime詳解

1资锰,runtime的核心—消息傳遞

在[object foo]在運(yùn)行的過(guò)程中會(huì)給object發(fā)送foo消息,但是這個(gè)消息可能會(huì)被轉(zhuǎn)發(fā)到另一個(gè)對(duì)象,也可能
并不會(huì)立即執(zhí)行foo這個(gè)發(fā)放的代碼。它在運(yùn)行時(shí)給object發(fā)送一個(gè)叫foo的消息泽铛,這個(gè)消息也許會(huì)由object來(lái)處理。也許會(huì)轉(zhuǎn)發(fā)給另一個(gè)對(duì)象辑鲤,或者不接收這個(gè)消息有另一個(gè)方法來(lái)實(shí)現(xiàn)這個(gè)消息
在編譯時(shí)盔腔,Object-C函數(shù)調(diào)用的語(yǔ)法會(huì)被編譯成一個(gè)C的函數(shù)調(diào)用 - objc_mesSend(),- objc_mesSend(object, @selector(), arg)

struct objc_object {  
    Class isa  OBJC_ISA_AVAILABILITY;
};

struct objc_class {  
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class;
    const char *name;
    long version;
    long info;
    long instance_size;
    struct objc_ivar_list *ivars;
    **struct objc_method_list **methodLists**;
    **struct objc_cache *cache**;
    struct objc_protocol_list *protocols;
#endif
};
struct objc_method_list {  
    struct objc_method_list *obsolete;
    int method_count;

#ifdef __LP64__
    int space;
#endif

    /* variable length structure */
    struct objc_method method_list[1];
};

struct objc_method {  
    SEL method_name;
    char *method_types;    /* a string representing argument/return types */
    IMP method_imp;
};

從API中可以看出oc的對(duì)象是一個(gè)指向objc_object(對(duì)象)結(jié)構(gòu)體的指針月褥。Class是一個(gè)指向objc_class(類)結(jié)構(gòu)體的指針弛随。每個(gè)結(jié)構(gòu)體中都包含一個(gè)isa指針,isa指針指向的類結(jié)構(gòu)宁赤。
objc_mesSend(object, @selector(), arg) 消息傳遞的過(guò)程是:先通過(guò)object的isa指針找到class,在class的object_cache找到改函數(shù)(object_cache是以method_name作為key,method_imp為value存儲(chǔ)的能夠直接查找)舀透,如果object_cache找不到會(huì)從class的methodList找改函數(shù)(從API中可以看出methodList其實(shí)是一個(gè)數(shù)組,查找效率太低)决左,再找不到會(huì)從他的superclass中找愕够,如果再找不到,如果我們不做特殊處理程序就會(huì)掛掉

2佛猛,消息傳遞補(bǔ)救:

如果消息未被實(shí)現(xiàn)時(shí)链烈,runtime調(diào)用resolveInstanceMethod實(shí)現(xiàn)

+ (BOOL)resolveInstanceMethod:(SEL)aSEL 
{
    //添加某個(gè)方法的實(shí)現(xiàn)
    if(aSEL == name){
        class_addMethod(Class cls, SEL name, IMP imp,
                    const char *types)
    }
    
    return YES;
}  

resolveInstanceMethod:方法返回YES時(shí)系統(tǒng)會(huì)從新發(fā)送一次消息,在返回YES之前添加沒有實(shí)現(xiàn)的方法
如果resolveInstanceMethod沒有特殊處理系統(tǒng)會(huì)調(diào)用forwardingTargetForSelector方法把消息轉(zhuǎn)發(fā)給另一個(gè)對(duì)象

- (id)forwardingTargetForSelector:(SEL)aSelector
{
    if(aSelector == SEL){
        return newObject;
    }
    return [super forwardingTargetForSelector:aSelector];
}

當(dāng)forwardingTargetForSelector返回nil,系統(tǒng)會(huì)發(fā)送methodSignatureForSelector消息獲得函數(shù)的返回值類型挚躯。如果 -methodSignatureForSelector: 返回 nil ,系統(tǒng)會(huì)發(fā)出doesNotRecognizeSelector消息擦秽,這時(shí)就無(wú)力會(huì)天了码荔。如果返回了一個(gè)函數(shù)簽名,Runtime 就會(huì)創(chuàng)建一個(gè) NSInvocation 對(duì)象并發(fā)送 -forwardInvocation: 消息給目標(biāo)對(duì)象感挥。

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
}

runtime使用的幾種場(chǎng)景

1缩搅,添加category屬性(關(guān)聯(lián)對(duì)象)

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
objc_getAssociatedObject(id object, const void *key)

程序會(huì)在執(zhí)行某個(gè)類的load,initialize方法時(shí)候執(zhí)行分類屬性
category不能夠直接添加屬性是因?yàn)閛c的對(duì)象objc_class結(jié)構(gòu)體大小是固定的触幼,不可能往這個(gè)結(jié)構(gòu)體中添加數(shù)據(jù)硼瓣,只能修改
category能夠直接添加方法是因?yàn)閛c的對(duì)象的objc_object結(jié)構(gòu)體里面的methodList是一個(gè)二維數(shù)組,所以可以修改*methodLists的值來(lái)增加成員方法置谦,雖沒辦法擴(kuò)展methodLists指向的內(nèi)存區(qū)域堂鲤,卻可以改變這個(gè)內(nèi)存區(qū)域的值

2,動(dòng)態(tài)得到屬性和方法

(1)獲取property屬性:

unsigned int outCount;
objc_property_t *objc_property_t = class_copyPropertyList([self class], &outCount)
for (int i = 0; i < outCount; i ++) {
    objc_property_t property = properties[i];
    NSString * propertyName = [[NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
}

(2)獲取所有屬性

unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
    Ivar ivar = ivars[i];
    NSString * name = [NSString stringWithUTF8String:ivar_getName(ivar)];
}

(3)得到方法列表class_copyMethodList

3媒峡,方法替換(method swizzling)

//newFoo將要替換的函數(shù)瘟栖,oldFoo被替換的函數(shù)
//獲取方法的實(shí)現(xiàn)
IMP newImp = class_getMethodImplementation([self class], @selector(newFoo));
//動(dòng)態(tài)添加方法
class_addMethod(Class, @selector(newFoo), newImp, types);
//動(dòng)態(tài)替換方法實(shí)現(xiàn)
class_replaceMethod(peopleClass, @selector(oldFoo), newImp, types);

method swizzling需要寫在load方法里面(load調(diào)用時(shí)機(jī)是被在到runtime時(shí),只調(diào)用一次谅阿,可以看看runtime源碼)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末半哟,一起剝皮案震驚了整個(gè)濱河市酬滤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌寓涨,老刑警劉巖盯串,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異戒良,居然都是意外死亡体捏,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蔬墩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)译打,“玉大人,你說(shuō)我怎么就攤上這事拇颅∽嗨荆” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵樟插,是天一觀的道長(zhǎng)韵洋。 經(jīng)常有香客問(wèn)我,道長(zhǎng)黄锤,這世上最難降的妖魔是什么搪缨? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮鸵熟,結(jié)果婚禮上副编,老公的妹妹穿的比我還像新娘。我一直安慰自己流强,他們只是感情好痹届,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著打月,像睡著了一般队腐。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上奏篙,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天柴淘,我揣著相機(jī)與錄音,去河邊找鬼秘通。 笑死为严,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的肺稀。 我是一名探鬼主播梗脾,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼盹靴!你這毒婦竟也來(lái)了炸茧?” 一聲冷哼從身側(cè)響起瑞妇,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎梭冠,沒想到半個(gè)月后辕狰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡控漠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年蔓倍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片盐捷。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡偶翅,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出碉渡,到底是詐尸還是另有隱情聚谁,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布滞诺,位于F島的核電站形导,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏习霹。R本人自食惡果不足惜朵耕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望淋叶。 院中可真熱鬧阎曹,春花似錦、人聲如沸煞檩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)形娇。三九已至,卻和暖如春筹误,著一層夾襖步出監(jiān)牢的瞬間桐早,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工厨剪, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哄酝,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓祷膳,卻偏偏與公主長(zhǎng)得像陶衅,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子直晨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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

  • 參考鏈接: http://www.cnblogs.com/ioshe/p/5489086.html 簡(jiǎn)介 Runt...
    樂(lè)樂(lè)的簡(jiǎn)書閱讀 2,135評(píng)論 0 9
  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉搀军,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,719評(píng)論 0 9
  • 簡(jiǎn)介 Runtime 又叫運(yùn)行時(shí)膨俐,是一套底層的 C 語(yǔ)言 API,其為 iOS 內(nèi)部的核心之一罩句,我們平時(shí)編寫的 O...
    專業(yè)男神經(jīng)閱讀 906評(píng)論 0 2
  • 運(yùn)行時(shí)是iOS中一個(gè)很重要的概念焚刺,iOS運(yùn)行過(guò)程中都會(huì)被轉(zhuǎn)化為runtime的C代碼執(zhí)行。例如[target do...
    蘿卜醬紫閱讀 398評(píng)論 0 3
  • System.IO.File.AppendAllText(@"c:\aa.txt", "dno111:" + 11...
    飛天豬Pony閱讀 552評(píng)論 0 1