iOS - runtime

開(kāi)篇

學(xué)習(xí)一門(mén)編程語(yǔ)言格二。不僅僅是用它來(lái)做項(xiàng)目歪沃,要懂得它的原理,這樣做心里踏實(shí)橄霉。想要更加深入的掌握OC或者做好iOS開(kāi)發(fā)窃爷,runtime無(wú)疑是打開(kāi)這個(gè)門(mén)的鑰匙。

那兒_并不遠(yuǎn)

OC語(yǔ)言中的runtime機(jī)制

  • OC語(yǔ)言是一門(mén)動(dòng)態(tài)語(yǔ)言姓蜂,他將很多其他的靜態(tài)語(yǔ)言在編譯和連接時(shí)期做的事放在了運(yùn)行時(shí)來(lái)處理按厘。所以說(shuō)OC不僅需要編譯器,他還需要一個(gè)運(yùn)行時(shí)系統(tǒng)來(lái)處理編譯的代碼钱慢,這個(gè)運(yùn)行時(shí)系統(tǒng)就像一個(gè)操作系統(tǒng)一樣逮京,讓所有的工作可以正常順利的進(jìn)行。
  • 運(yùn)行時(shí)系統(tǒng)就是我們所說(shuō)的運(yùn)行時(shí)機(jī)制(Objc Runtime)束莫,它是一套由C語(yǔ)言和匯編語(yǔ)言開(kāi)發(fā)的開(kāi)源庫(kù)懒棉,由很多數(shù)據(jù)結(jié)構(gòu)和函數(shù)組成,通過(guò)runtime我們可以動(dòng)態(tài)的修改類(lèi)览绿,對(duì)象中的屬性或者方法策严,無(wú)論公有私有。

類(lèi)和對(duì)象的基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)

struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;  // 指針饿敲,實(shí)例的isa指向類(lèi)的對(duì)象妻导,類(lèi)對(duì)象的isa指向元類(lèi)。

#if !__OBJC2__
    Class super_class                                        OBJC2_UNAVAILABLE;  //  父類(lèi) 
    const char *name                                         OBJC2_UNAVAILABLE;  //  類(lèi)名
    long version                                             OBJC2_UNAVAILABLE;  //  類(lèi)的版本信息,默認(rèn)為0
    long info                                                OBJC2_UNAVAILABLE;  //  類(lèi)信息,供運(yùn)行期使用的一些位標(biāo)識(shí)
    long instance_size                                       OBJC2_UNAVAILABLE;  //  該類(lèi)的實(shí)例變量大小
    struct objc_ivar_list *ivars                             OBJC2_UNAVAILABLE;  //  該類(lèi)的成員變量鏈表
    struct objc_method_list **methodLists                    OBJC2_UNAVAILABLE;  //  方法定義鏈表 
    struct objc_cache *cache                                 OBJC2_UNAVAILABLE;  //  方法緩存
    struct objc_protocol_list *protocols                     OBJC2_UNAVAILABLE;  //  協(xié)議鏈表
#endif

} OBJC2_UNAVAILABLE;

在上述的runtime封裝的類(lèi)的數(shù)據(jù)結(jié)構(gòu)中通常我們會(huì)對(duì)一下幾個(gè)變量感興趣:

  • isa : 學(xué)習(xí)過(guò)C語(yǔ)言的同學(xué)知道指針的作用诀蓉,指針其實(shí)就是我們將數(shù)據(jù)存放到內(nèi)存中的地址栗竖,同理isa就是我們創(chuàng)建的類(lèi)或者對(duì)象存放在內(nèi)存中的地址(在這里我們需要注意,在OC中渠啤,所有的類(lèi)自身也是一個(gè)對(duì)象,這個(gè)對(duì)象的Class里面也有一個(gè)isa指針添吗,它指向metaClass(元類(lèi)))沥曹,當(dāng)我們向一個(gè)OC對(duì)象發(fā)送消息時(shí),運(yùn)行時(shí)庫(kù)會(huì)根據(jù)實(shí)例對(duì)象的isa指針找到這個(gè)實(shí)例對(duì)象所屬的類(lèi),Runtime庫(kù)會(huì)在類(lèi)的方法列表及父類(lèi)的方法列表中尋找與消息對(duì)應(yīng)的selector指向的方法.找到后即運(yùn)行這個(gè)方法.
  • cache : 用于緩存我們最近調(diào)用的方法,當(dāng)我們調(diào)用一個(gè)對(duì)象的方法時(shí)妓美,會(huì)根據(jù)isa指針找到這個(gè)對(duì)象僵腺,然后遍歷方法鏈表,但是壶栋,一個(gè)對(duì)象中會(huì)存在很多的方法辰如,很多都是很少用的,如果每調(diào)用一次對(duì)象的方法都去遍歷一次方法鏈表贵试,勢(shì)必會(huì)降低性能琉兜,所以這是,我們把常用的方法緩存到cache中毙玻,這樣每次調(diào)用方法時(shí)豌蟋,runtime會(huì)優(yōu)先到cache中查找,如果沒(méi)有找到再去遍歷方法鏈表桑滩。

獲取屬性梧疲,變量,方法运准,協(xié)議鏈表

  • 這里先創(chuàng)建了一個(gè)Person類(lèi)幌氮,下面分別為Person.h,Person.m文件
#import <Foundation/Foundation.h>

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *sex;
@property (nonatomic, copy) NSString *age;

- (void)sayHi;
@end
#import "Person.h"
#import <objc/runtime.h>

@interface Person ()
@property (nonatomic, copy) NSString *privacy;
@end

@implementation Person

- (void)sayHi {
    NSLog(@"my name is coco");
}

 
}

@end
  • 接下來(lái)我們?cè)趤?lái)獲取Person中鏈表
// 獲取屬性
- (void)runtimeTestForProperName {
    unsigned int count;
    objc_property_t *properList = class_copyPropertyList([Person class], &count);
    for (unsigned i = 0; i < count; i++) {
        const char *properName = property_getName(properList[i]);
        NSLog(@"屬性名稱(chēng) %@", [NSString stringWithUTF8String:properName]);
    }
}
// 獲取變量
- (void)runtimeTestForIvar {
    unsigned int count;
    Ivar *ivarList = class_copyIvarList([Person class], &count);
    for (unsigned int i = 0; i < count; i++) {
        const char *ivarName = ivar_getName(ivarList[i]);
        NSLog(@"成員變量 %@", [NSString stringWithUTF8String:ivarName]);
    }
}
// 獲取方法
- (void)runtimeForMethod {
    unsigned int count;
    Method *methodList = class_copyMethodList([Person class], &count);
    for (unsigned int i = 0; i < count; i++) {
        Method method = methodList[i];
        SEL methodName = method_getName(method);
        NSLog(@"方法名稱(chēng) %@", NSStringFromSelector(methodName));
    }
}
// 獲取協(xié)議
- (void)runtimeForProtocol {
    unsigned int count;
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([Person class], &count);
    for (unsigned int i; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"協(xié)議方法 %@", [NSString stringWithUTF8String:protocolName]);
    }
}

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

  • 向person對(duì)象中添加school屬性
- (void)setupProperty {
 
    /** objc_setAssociatedObject: 方法的參數(shù)說(shuō)明
     object: 添加的對(duì)象
     key: 添加的屬性名稱(chēng)(key值胁澳,獲取時(shí)會(huì)用到)
     value: 添加對(duì)應(yīng)的屬性值 (value值)
     policy: 添加的策略(屬性對(duì)應(yīng)的修飾)
     **/
    Person *person = [[Person alloc] init];
    objc_setAssociatedObject(person, @"school", @"吉林師范", OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    NSString *associatedValue = objc_getAssociatedObject(person, @"school");
    NSLog(@"設(shè)置的屬性 :%@", associatedValue);
    
}

其中 我們可以點(diǎn)進(jìn)policy這個(gè)參數(shù)中该互,我們可以清除的看到這是關(guān)于我們定義屬性的時(shí)候用到的幾種修飾。

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
    OBJC_ASSOCIATION_ASSIGN = 0,      
    OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1,
    OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
    OBJC_ASSOCIATION_RETAIN = 01401,  
    OBJC_ASSOCIATION_COPY = 01403
};
  • 上述是向現(xiàn)有類(lèi)中添加屬性听哭,如果你查看runtime的API你就會(huì)發(fā)現(xiàn)慢洋,還有一個(gè) class_addIvar:方法可以添加變量,但是這個(gè)方法不支持現(xiàn)有類(lèi)的添加陆盘,官方是這樣說(shuō)的
// 此方法僅可在objc_allocateclasspair方法和objc_registerclasspair之間使用
* This function may only be called after objc_allocateClassPair and before objc_registerClassPair. 
// 不支持在現(xiàn)有類(lèi)中添加一個(gè)實(shí)例變量
 * Adding an instance variable to an existing class is not supported.

接下來(lái)我們通過(guò)動(dòng)態(tài)創(chuàng)建一個(gè)類(lèi)普筹,并向其中添加變量“恚看代碼

- (void)createMyClass
{

    /** objc_allocateClassPair: 參數(shù)說(shuō)明
     superclass: 父類(lèi)
     name: 類(lèi)名
     extraButes: 額外分配的字節(jié)(寫(xiě)0就可以)
     **/

    /** class_addIvar: 參數(shù)說(shuō)明
     cls: 類(lèi)
     name: 變量名
     size: 變量的所占內(nèi)存的字節(jié)
     alignment: 對(duì)齊方式(寫(xiě)0就可以)
     types: 變量類(lèi)型
     **/

    // rumtime 動(dòng)態(tài)創(chuàng)建一個(gè)類(lèi)
    Class MyClass = objc_allocateClassPair([NSObject class], "myclass", 0);
    / /  添加一個(gè)NSString的變量
    if (class_addIvar(MyClass, "motto", sizeof(NSString *), 0, "@")) {
        NSLog(@"add ivar success");
    }
    //  注冊(cè)這個(gè)類(lèi)到runtime系統(tǒng)中才可以使用它
    objc_registerClassPair(MyClass);

    //  生成了一個(gè)實(shí)例化對(duì)象
    id myobj = [[MyClass alloc] init];
    NSString *string = @"it's my live";
    //   賦值
    [myobj setValue: string forKey:@"motto"];
    Ivar ivarOFset = class_getInstanceVariable([myobj class], "motto");
    id value = object_getIvar(myobj, ivarOFset);
    NSLog(@"添加的變量的值  = %@", value);
}

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

- (void)setupMethod {

    /** class_addMethod: 參數(shù)說(shuō)明
     cls: 類(lèi)
     name: 方法名
     imp: 方法的地址
     types: 方法類(lèi)型
     **/    

   // 獲取 song 方法的類(lèi)型
    Method method = class_getInstanceMethod([self class], @selector(song));
    const char *methodType = method_getTypeEncoding(method);
    NSLog(@"參數(shù)類(lèi)型 %@", [NSString stringWithUTF8String:methodType]);
    
    // 添加 song 方法
    class_addMethod([Person class], @selector(song), class_getMethodImplementation([self class], @selector(song)), methodType);
    // 創(chuàng)建實(shí)例對(duì)象
    Person *person = [[Person alloc] init];
    // 調(diào)用添加的方法
    [person performSelector:@selector(song) withObject:nil];
    
}

- (void)song {
    NSLog(@"大家好太防,為大家來(lái)一首 - 《那就這樣吧》");  
}

通過(guò)ruantime交換,替換方法

  • 交換兩個(gè)方法(方法A酸员,方法B)蜒车,當(dāng)兩個(gè)方法交換成功后,調(diào)用方法A后就會(huì)執(zhí)行方法B中的代碼幔嗦。
  • 替換方法酿愧,很好理解,就是將方法A替換成方法B邀泉。

** 無(wú)論是交換還是替換方法嬉挡,實(shí)質(zhì)rumtime就是將方法的指針做了交換或者替換 **

- (void)exchangeMethod {
    
    // 獲取兩個(gè)方法的Method
    Method funcA = class_getInstanceMethod([self class], @selector(functionA));
    Method funcB = class_getInstanceMethod([self class], @selector(functionB));
    // 交換兩個(gè)方法
    method_exchangeImplementations(funcA, funcB);
    [self functionA];
    [self functionB];

    /** 官方文檔是這樣介紹 method_exchangeImplementations 方法實(shí)現(xiàn)的:
    * @note This is an atomic version of the following:
    *  \code 
    *  IMP imp1 = method_getImplementation(m1);
    *  IMP imp2 = method_getImplementation(m2);
    *  method_setImplementation(m1, imp2);
    *  method_setImplementation(m2, imp1);
    *  \endcode
     **/
    
}

- (void)replaceMethod {

    Method funcB = class_getInstanceMethod([self class], @selector(functionB));
   // 將方法A替換成方法B
    class_replaceMethod([self class], @selector(functionA), method_getImplementation(funcB), method_getTypeEncoding(funcB));
    [self functionA];
    [self functionB];

}

- (void)functionA {
    NSLog(@"我是方法A");
}

- (void)functionB {
    NSLog(@"我是方法B");
}

總結(jié)

通過(guò)上述的介紹我想大家對(duì)runtime有了一個(gè)初步的了解钝鸽,同時(shí)對(duì)于OC也會(huì)又一個(gè)更深的認(rèn)識(shí)。上面代碼的運(yùn)行結(jié)果我就不給大家一一截圖了庞钢,我都是驗(yàn)證后才寫(xiě)在博客上的拔恰。runtime中還有很多方法,但是都是大同小異基括,用法都是可以舉一反三的颜懊。希望這篇博客會(huì)對(duì)大家有所啟發(fā),同時(shí)歡迎大家提出建議和不同的看法风皿,見(jiàn)解河爹,分享可以讓我們共同進(jìn)步。

最后編輯于
?著作權(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)店門(mén)拓挥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)唠梨,“玉大人,你說(shuō)我怎么就攤上這事侥啤〉卑龋” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵盖灸,是天一觀的道長(zhǎng)蚁鳖。 經(jīng)常有香客問(wèn)我,道長(zhǎng)赁炎,這世上最難降的妖魔是什么醉箕? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮徙垫,結(jié)果婚禮上讥裤,老公的妹妹穿的比我還像新娘。我一直安慰自己姻报,他們只是感情好己英,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著吴旋,像睡著了一般剧辐。 火紅的嫁衣襯著肌膚如雪寒亥。 梳的紋絲不亂的頭發(fā)上邮府,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天荧关,我揣著相機(jī)與錄音,去河邊找鬼褂傀。 笑死忍啤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的仙辟。 我是一名探鬼主播同波,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼叠国!你這毒婦竟也來(lái)了未檩?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤粟焊,失蹤者是張志新(化名)和其女友劉穎冤狡,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(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
  • 文/蒙蒙 一顽决、第九天 我趴在偏房一處隱蔽的房頂上張望短条。 院中可真熱鬧,春花似錦才菠、人聲如沸茸时。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)可都。三九已至缓待,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渠牲,已是汗流浹背旋炒。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(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)容

  • runtime 和 runloop 作為一個(gè)程序員進(jìn)階是必須的鹦付,也是非常重要的尚粘, 在面試過(guò)程中是經(jīng)常會(huì)被問(wèn)到的, ...
    SOI閱讀 21,808評(píng)論 3 63
  • 簡(jiǎn)介(http://www.cnblogs.com/ioshe/p/5489086.html) Runtime 又...
    清風(fēng)沐沐閱讀 507評(píng)論 0 0
  • 技 術(shù) 文 章 / 超 人 Runtime(運(yùn)行時(shí)機(jī)制)概念 Object-C 是面向?qū)ο蟮恼Z(yǔ)言敲长,C是面向結(jié)構(gòu)也就...
    樹(shù)下敲代碼的超人閱讀 1,007評(píng)論 0 16
  • 轉(zhuǎn)自http://www.reibang.com/p/af485c916df1 1.概要 對(duì)于Runti...
    風(fēng)尋月覓閱讀 592評(píng)論 0 0
  • 前言 運(yùn)行時(shí)可以說(shuō)是 Objective-C 這門(mén)語(yǔ)言的一個(gè)核心部分郎嫁,看了許多博客,也反反復(fù)復(fù)看了多次源碼(蘋(píng)果開(kāi)...
    zerocc2014閱讀 4,522評(píng)論 6 75