日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) >

Runtime 总结

發(fā)布時(shí)間:2025/7/14 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Runtime 总结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

參考文章

1. `文/滕先洪(簡(jiǎn)書(shū)作者)原文鏈接:http://www.jianshu.com/p/ab966e8a82e2著作權(quán)歸作者所有,轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán),并標(biāo)注“簡(jiǎn)書(shū)作者”[下載地址] (https://github.com/XHTeng/XHRuntimeDemo)`2.`http://www.code4app.com/forum.php?mod=viewthread&tid=8241&highlight=runtime`

什么是runtime

runtime 是 OC底層的一套C語(yǔ)言的API(引入<objc/runtime.h> 或<objc/message.h> ),編譯器最終都會(huì)將OC代碼轉(zhuǎn)化為運(yùn)行時(shí)代碼,通過(guò)終端命令編譯.m 文件:clang -rewrite-objc xxx.m可以看到編譯后的xxx.cpp(C++文件)。

  • RunTime簡(jiǎn)稱(chēng)運(yùn)行時(shí),就是系統(tǒng)在運(yùn)行的時(shí)候的一些機(jī)制,其中最主要的是消息機(jī)制。
  • 對(duì)于C語(yǔ)言,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù),編譯完成之后直接順序執(zhí)行,無(wú)任何二義性。
  • OC的函數(shù)調(diào)用成為消息發(fā)送。屬于動(dòng)態(tài)調(diào)用過(guò)程。在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)(事實(shí)證明,在編 譯階段,OC可以調(diào)用任何函數(shù),即使這個(gè)函數(shù)并未實(shí)現(xiàn),只要申明過(guò)就不會(huì)報(bào)錯(cuò)。而C語(yǔ)言在編譯階段就會(huì)報(bào)錯(cuò))。
  • 只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱(chēng)找 到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。
  • 我們寫(xiě)的oc代碼,它在運(yùn)行的時(shí)候也是轉(zhuǎn)換成了runtime方式運(yùn)行的,更好的理解runtime,也能幫我們更深的掌握oc語(yǔ)言。
  • 每一個(gè)oc的方法,底層必然有一個(gè)與之對(duì)應(yīng)的runtime方法。
  • 當(dāng)我們用OC寫(xiě)下這樣一段代碼[tableView cellForRowAtIndexPath:indexPath];
  • 在編譯時(shí)RunTime會(huì)將上述代碼轉(zhuǎn)化成[發(fā)送消息]objc_msgSend(tableView, @selector(cellForRowAtIndexPath,indexPath);

runtime的作用

  • 動(dòng)態(tài)交換兩個(gè)方法的實(shí)現(xiàn)(特別是交換系統(tǒng)自帶的方法);
  • 動(dòng)態(tài)添加對(duì)象的成員變量和成員方法;
  • 獲得某個(gè)類(lèi)的所有成員方法、所有成員變量;
  • 具體一點(diǎn)就是

  • 獲得某個(gè)類(lèi)的所有成員方法、所有成員變量;
  • 攔截系統(tǒng)自帶的方法調(diào)用(Swizzle 黑魔法),比如攔截imageNamed:、viewDidLoad、alloc;
  • 實(shí)現(xiàn)分類(lèi)也可以增加屬性;
  • 實(shí)現(xiàn)NSCoding的自動(dòng)歸檔和自動(dòng)解檔;
  • 實(shí)現(xiàn)字典和模型的自動(dòng)轉(zhuǎn)換。
  • 案例匯總

    案例一:方法簡(jiǎn)單的交換

    需要用到的方法 <objc/runtime.h>

    • 獲得某個(gè)類(lèi)的類(lèi)方法
      Method class_getClassMethod(Class cls , SEL name)
    • 獲得某個(gè)類(lèi)的實(shí)例對(duì)象方法
      Method class_getInstanceMethod(Class cls , SEL name)
    • 交換兩個(gè)方法的實(shí)現(xiàn)
      void method_exchangeImplementations(Method m1 , Method m2)

    創(chuàng)建一個(gè)Person類(lèi),類(lèi)中實(shí)現(xiàn)以下兩個(gè)類(lèi)方法,并在.h 文件中聲明

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

    調(diào)用方法,并通過(guò)runtime實(shí)現(xiàn)方法交換

    - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{person *aperson = [[person alloc]init];[person run]; [person study];// 獲取兩個(gè)類(lèi)的類(lèi)方法 Method m1 = class_getClassMethod([person class], @selector(run)); Method m2 = class_getClassMethod([person class], @selector(study)); // 開(kāi)始交換方法實(shí)現(xiàn) method_exchangeImplementations(m1, m2); // 交換后,先打印學(xué)習(xí),再打印跑! [person run]; [person study];}

    打印結(jié)果為:

    2016-07-11 14:10:28.033 runtime demo[37610:2393684] 跑 2016-07-11 14:10:28.034 runtime demo[37610:2393684] 學(xué)習(xí) 2016-07-11 14:10:28.034 runtime demo[37610:2393684] 學(xué)習(xí) 2016-07-11 14:10:28.034 runtime demo[37610:2393684] 跑

    案例二:攔截并替換方法

    需求:比如iOS6 升級(jí) iOS7 后需要版本適配,根據(jù)不同系統(tǒng)使用不同樣式圖片(擬物化和扁平化),如何通過(guò)不去手動(dòng)一個(gè)個(gè)修改每個(gè)UIImage的imageNamed:方法就可 以實(shí)現(xiàn)為該方法中加入版本判斷語(yǔ)句?

    步驟:
    1、為UIImage建一個(gè)分類(lèi)(UIImage+Category)
    2、在分類(lèi)中實(shí)現(xiàn)一個(gè)自定義方法,方法中寫(xiě)要在系統(tǒng)方法中加入的語(yǔ)句,比如版本判斷

    + (UIImage *)xh_imageNamed:(NSString *)name { double version = [[UIDevice currentDevice].systemVersion doubleValue]; if (version >= 7.0) {// 如果系統(tǒng)版本是7.0以上,使用另外一套文件名結(jié)尾是‘_os7’的扁平化圖片name = [name stringByAppendingString:@"_os7"]; } return [UIImage xh_imageNamed:name]; }

    3、分類(lèi)中重寫(xiě)UIImage的load方法,實(shí)現(xiàn)方法的交換(只要能讓其執(zhí)行一次方法交換語(yǔ)句,load再合適不過(guò)了)

    + (void)load { // 獲取兩個(gè)類(lèi)的類(lèi)方法 Method m1 = class_getClassMethod([UIImage class], @selector(imageNamed:)); Method m2 = class_getClassMethod([UIImage class], @selector(xh_imageNamed:)); // 開(kāi)始交換方法實(shí)現(xiàn) method_exchangeImplementations(m1, m2); }

    注意:自定義方法中最后一定要再調(diào)用一下系統(tǒng)的方法,讓其有加載圖片的功能,但是由于方法交換,系統(tǒng)的方法名已經(jīng)變成了我們自定義的方法名(有點(diǎn)繞,就是用我們的名字能調(diào)用系統(tǒng)的方法,用系統(tǒng)的名字能調(diào)用我們的方法),這就實(shí)現(xiàn)了系統(tǒng)方法的攔截!

    利用以上思路,我們還可以給 NSObject 添加分類(lèi),統(tǒng)計(jì)創(chuàng)建了多少個(gè)對(duì)象,給控制器添加分類(lèi),統(tǒng)計(jì)有創(chuàng)建了多少個(gè)控制器,特別是公司需求總變的時(shí)候,在一些原有控件或模塊上添加一個(gè)功能,建議使用該方法!

    案例三:在分類(lèi)中設(shè)置屬性,給任何一個(gè)對(duì)象設(shè)置屬性

    眾所周知,分類(lèi)中是無(wú)法設(shè)置屬性的,如果在分類(lèi)的聲明中寫(xiě)@property 只能為其生成get 和 set 方法的聲明,但無(wú)法生成成員變量,就是雖然點(diǎn)語(yǔ)法能調(diào)用出來(lái),但程序執(zhí)行后會(huì)crash,有人會(huì)想到使用全局變量呢?比如這樣:

    int _age;- (int )age {return _age; }- (void)setAge:(int)age {_age = age; }

    但是全局變量程序整個(gè)執(zhí)行過(guò)程中內(nèi)存中只有一份,我們創(chuàng)建多個(gè)對(duì)象修改其屬性值都會(huì)修改同一個(gè)變量,這樣就無(wú)法保證像屬性一樣每個(gè)對(duì)象都擁有其自己的屬性值。這時(shí)我們就需要借助runtime為分類(lèi)增加屬性的功能了。

    需要用到的方法 <objc/runtime.h>

    • set方法,將值value 跟對(duì)象object 關(guān)聯(lián)起來(lái)(將值value 存儲(chǔ)到對(duì)象object 中)
      參數(shù) object:給哪個(gè)對(duì)象設(shè)置屬性
      參數(shù) key:一個(gè)屬性對(duì)應(yīng)一個(gè)Key,將來(lái)可以通過(guò)key取出這個(gè)存儲(chǔ)的值,key 可以是任何類(lèi)型:double、int 等,建議用char 可以節(jié)省字節(jié)
      參數(shù) value:給屬性設(shè)置的值
      參數(shù)policy:存儲(chǔ)策略 (assign 、copy 、 retain就是strong)
      void objc_setAssociatedObject(id object , const void *key ,id value ,objc_AssociationPolicy policy)
    • 利用參數(shù)key 將對(duì)象object中存儲(chǔ)的對(duì)應(yīng)值取出來(lái)
      id objc_getAssociatedObject(id object , const void *key)

    步驟:
    1、創(chuàng)建一個(gè)分類(lèi),比如給任何一個(gè)對(duì)象都添加一個(gè)name屬性,就是NSObject添加分類(lèi)(NSObject+Category)

    2、先在.h 中@property 聲明出get 和 set 方法,方便點(diǎn)語(yǔ)法調(diào)用

    @property(nonatomic,copy)NSString *name;

    3、在.m 中重寫(xiě)set 和 get 方法,內(nèi)部利用runtime 給屬性賦值和取值

    char nameKey;- (void)setName:(NSString *)name {// 將某個(gè)值跟某個(gè)對(duì)象關(guān)聯(lián)起來(lái),將某個(gè)值存儲(chǔ)到某個(gè)對(duì)象中objc_setAssociatedObject(self, &nameKey, name, OBJC_ASSOCIATION_COPY_NONATOMIC); }- (NSString *)name {return objc_getAssociatedObject(self, &nameKey); }

    案例四:獲得一個(gè)類(lèi)的所有成員變量

    最典型的用法就是一個(gè)對(duì)象在歸檔和解檔的 encodeWithCoder和initWithCoder:方法中需要該對(duì)象所有的屬性進(jìn)行decodeObjectForKey: 和 encodeObject:,通過(guò)runtime我們聲明中無(wú)論寫(xiě)多少個(gè)屬性,都不需要再修改實(shí)現(xiàn)中的代碼了。

    需要用到的方法 <objc/runtime.h>

    • 獲得某個(gè)類(lèi)的所有成員變量(outCount 會(huì)返回成員變量的總數(shù))
      參數(shù):
      1、哪個(gè)類(lèi)
      2、放一個(gè)接收值的地址,用來(lái)存放屬性的個(gè)數(shù)
      3、返回值:存放所有獲取到的屬性,通過(guò)下面兩個(gè)方法可以調(diào)出名字和類(lèi)型
    Ivar *class_copyIvarList(Class cls , unsigned int *outCount)
    • 獲得成員變量的名字
    const char *ivar_getName(Ivar v)
    • 獲得成員變量的類(lèi)型
    const char *ivar_getTypeEndcoding(Ivar v)

    獲取Person類(lèi)中所有成員變量的名字和類(lèi)型

    unsigned int outCount = 0; Ivar *ivars = class_copyIvarList([Person class], &outCount);// 遍歷所有成員變量 for (int i = 0; i < outCount; i++) {// 取出i位置對(duì)應(yīng)的成員變量Ivar ivar = ivars[i];const char *name = ivar_getName(ivar);const char *type = ivar_getTypeEncoding(ivar);NSLog(@"成員變量名:%s 成員變量類(lèi)型:%s",name,type); } // 注意釋放內(nèi)存! free(ivars);

    利用runtime 獲取所有屬性來(lái)重寫(xiě)歸檔解檔方法

    如果你實(shí)現(xiàn)過(guò)自定義模型數(shù)據(jù)持久化的過(guò)程,那么你也肯定明白,如果一個(gè)模型有許多個(gè)屬性,那么我們需要對(duì)每個(gè)屬性都實(shí)現(xiàn)一遍encodeObject 和 decodeObjectForKey方法,如果這樣的模型又有很多個(gè),這還真的是一個(gè)十分麻煩的事情。下面來(lái)看看簡(jiǎn)單的實(shí)現(xiàn)方式。
    假設(shè)現(xiàn)在有一個(gè)Movie類(lèi),有3個(gè)屬性,它的h文件這這樣的

    #import <Foundation/Foundation.h>//1. 如果想要當(dāng)前類(lèi)可以實(shí)現(xiàn)歸檔與反歸檔,需要遵守一個(gè)協(xié)議NSCoding @interface Movie : NSObject<NSCoding>@property (nonatomic, copy) NSString *movieId; @property (nonatomic, copy) NSString *movieName; @property (nonatomic, copy) NSString *pic_url;@end

    如果是正常寫(xiě)法, m文件應(yīng)該是這樣的:

    #import "Movie.h" @implementation Movie- (void)encodeWithCoder:(NSCoder *)aCoder {[aCoder encodeObject:_movieId forKey:@"id"];[aCoder encodeObject:_movieName forKey:@"name"];[aCoder encodeObject:_pic_url forKey:@"url"];}- (id)initWithCoder:(NSCoder *)aDecoder {if (self = [super init]) {self.movieId = [aDecoder decodeObjectForKey:@"id"];self.movieName = [aDecoder decodeObjectForKey:@"name"];self.pic_url = [aDecoder decodeObjectForKey:@"url"];}return self; } @end

    如果這里有100個(gè)屬性,那么我們也只能把100個(gè)屬性都給寫(xiě)一遍。
    不過(guò)你會(huì)使用runtime后,這里就有更簡(jiǎn)便的方法。
    下面看看runtime的實(shí)現(xiàn)方式:

    #import "Movie.h" #import <objc/runtime.h> @implementation Movie- (void)encodeWithCoder:(NSCoder *)encoder{unsigned int count = 0;Ivar *ivars = class_copyIvarList([Movie class], &count);for (int i = 0; i<count; i++) {// 取出i位置對(duì)應(yīng)的成員變量Ivar ivar = ivars;// 查看成員變量const char *name = ivar_getName(ivar);// 歸檔NSString *key = [NSString stringWithUTF8String:name];id value = [self valueForKey:key];[encoder encodeObject:value forKey:key];}free(ivars); }- (id)initWithCoder:(NSCoder *)decoder {if (self = [super init]) {unsigned int count = 0;Ivar *ivars = class_copyIvarList([Movie class], &count);for (int i = 0; i<count; i++) {// 取出i位置對(duì)應(yīng)的成員變量Ivar ivar = ivars;// 查看成員變量const char *name = ivar_getName(ivar);// 歸檔NSString *key = [NSString stringWithUTF8String:name];id value = [decoder decodeObjectForKey:key];// 設(shè)置到成員變量身上[self setValue:value forKey:key];}free(ivars);} return self; } @end

    這樣的方式實(shí)現(xiàn),不管有多少個(gè)屬性,寫(xiě)這幾行代碼就搞定了。怎么,還嫌麻煩,下面看看更加簡(jiǎn)便的方法:兩句代碼搞定。
    我們把encodeWithCoder 和 initWithCoder這兩個(gè)方法抽成宏

    #import "Movie.h" #import <objc/runtime.h>#define encodeRuntime(A) unsigned int count = 0; Ivar *ivars = class_copyIvarList([A class], &count); for (int i = 0; i<count; i++) { Ivar ivar = ivars; const char *name = ivar_getName(ivar); NSString *key = [NSString stringWithUTF8String:name]; id value = [self valueForKey:key]; [encoder encodeObject:value forKey:key]; } free(ivars);#define initCoderRuntime(A) if (self = [super init]) { unsigned int count = 0; Ivar *ivars = class_copyIvarList([A class], &count); for (int i = 0; i<count; i++) { Ivar ivar = ivars; const char *name = ivar_getName(ivar); NSString *key = [NSString stringWithUTF8String:name]; id value = [decoder decodeObjectForKey:key]; [self setValue:value forKey:key]; } free(ivars); } return self;@implementation Movie- (void)encodeWithCoder:(NSCoder *)encoder{encodeRuntime(Movie) }- (id)initWithCoder:(NSCoder *)decoder {initCoderRuntime(Movie) } @end

    我們可以把這兩個(gè)宏單獨(dú)放到一個(gè)文件里面,這里以后需要進(jìn)行數(shù)據(jù)持久化的模型都可以直接使用這兩個(gè)宏。

    案例五:利用runtime 獲取所有屬性來(lái)進(jìn)行字典轉(zhuǎn)模型

    以往我們都是利用KVC進(jìn)行字典轉(zhuǎn)模型,但是它還是有一定的局限性,例如:模型屬性和鍵值對(duì)對(duì)應(yīng)不上會(huì)crash(雖然可以重寫(xiě)setValue:forUndefinedKey:方法防止報(bào)錯(cuò)),模型屬性是一個(gè)對(duì)象或者數(shù)組時(shí)不好處理等問(wèn)題,所以無(wú)論是效率還是功能上,利用runtime進(jìn)行字典轉(zhuǎn)模型都是比較好的選擇。

    字典轉(zhuǎn)模型我們需要考慮三種特殊情況:
    1.當(dāng)字典的key和模型的屬性匹配不上
    2.模型中嵌套模型(模型屬性是另外一個(gè)模型對(duì)象)
    3.數(shù)組中裝著模型(模型的屬性是一個(gè)數(shù)組,數(shù)組中是一個(gè)個(gè)模型對(duì)象)

    字典轉(zhuǎn)模型的應(yīng)用可以說(shuō)是每個(gè)app必然會(huì)使用的場(chǎng)景,雖然實(shí)現(xiàn)的方式略有不同,但是原理都是一致的:遍歷模型中所有屬性,根據(jù)模型的屬性名,去字典中查找key,取出對(duì)應(yīng)的值,給模型的屬性賦值。
    像幾個(gè)出名的開(kāi)源庫(kù):JSONModel,MJExtension等都是通過(guò)這種方式實(shí)現(xiàn)的。

    • 先實(shí)現(xiàn)最外層的屬性轉(zhuǎn)換

      // 創(chuàng)建對(duì)應(yīng)模型對(duì)象
      id objc = [[self alloc] init];

      unsigned int count = 0;// 1.獲取成員屬性數(shù)組 Ivar *ivarList = class_copyIvarList(self, &count);// 2.遍歷所有的成員屬性名,一個(gè)一個(gè)去字典中取出對(duì)應(yīng)的value給模型屬性賦值 for (int i = 0; i < count; i++) {// 2.1 獲取成員屬性Ivar ivar = ivarList;// 2.2 獲取成員屬性名 C -> OC 字符串NSString *ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];// 2.3 _成員屬性名 => 字典keyNSString *key = [ivarName substringFromIndex:1];// 2.4 去字典中取出對(duì)應(yīng)value給模型屬性賦值id value = dict[key];// 獲取成員屬性類(lèi)型NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];}

    如果模型比較簡(jiǎn)單,只有NSString,NSNumber等,這樣就可以搞定了。但是如果模型含有NSArray,或者NSDictionary等,那么我們還需要進(jìn)行第二步轉(zhuǎn)換。

    • 內(nèi)層數(shù)組,字典的轉(zhuǎn)換

      if ([value isKindOfClass:[NSDictionary class]] && ![ivarType containsString:@"NS"]) { // 是字典對(duì)象,并且屬性名對(duì)應(yīng)類(lèi)型是自定義類(lèi)型// 處理類(lèi)型字符串 @\"User\" -> UserivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];// 自定義對(duì)象,并且值是字典// value:user字典 -> User模型// 獲取模型(user)類(lèi)對(duì)象Class modalClass = NSClassFromString(ivarType);// 字典轉(zhuǎn)模型if (modalClass) {// 字典轉(zhuǎn)模型 uservalue = [modalClass objectWithDict:value];}}if ([value isKindOfClass:[NSArray class]]) {// 判斷對(duì)應(yīng)類(lèi)有沒(méi)有實(shí)現(xiàn)字典數(shù)組轉(zhuǎn)模型數(shù)組的協(xié)議if ([self respondsToSelector:@selector(arrayContainModelClass)]) {// 轉(zhuǎn)換成id類(lèi)型,就能調(diào)用任何對(duì)象的方法id idSelf = self;// 獲取數(shù)組中字典對(duì)應(yīng)的模型NSString *type = [idSelf arrayContainModelClass][key];// 生成模型Class classModel = NSClassFromString(type);NSMutableArray *arrM = [NSMutableArray array];// 遍歷字典數(shù)組,生成模型數(shù)組for (NSDictionary *dict in value) {// 字典轉(zhuǎn)模型id model = [classModel objectWithDict:dict];[arrM addObject:model];}// 把模型數(shù)組賦值給valuevalue = arrM;}}

    我自己覺(jué)得系統(tǒng)自帶的KVC模式字典轉(zhuǎn)模型就挺好的,假設(shè)movie是一個(gè)模型對(duì)象,dict 是一個(gè)需要轉(zhuǎn)化的 [movie setValuesForKeysWithDictionary:dict]; 這個(gè)是系統(tǒng)自帶的字典轉(zhuǎn)模型方法,個(gè)人感覺(jué)也還是挺好用的,不過(guò)使用這個(gè)方法的時(shí)候需要在模型里面再實(shí)現(xiàn)一個(gè)方法才行,
    - (void)setValue: (id)value forUndefinedKey: (NSString *)key

    重寫(xiě)這個(gè)方法為了實(shí)現(xiàn)兩個(gè)目的:

  • 模型中的屬性和字典中的key不一致的情況,比如字典中有個(gè)id,我們需要把它賦值給uid屬性;
  • 字典中屬性比模型的屬性還多的情況。如果出現(xiàn)以上兩種情況而沒(méi)有實(shí)現(xiàn)這個(gè)方法的話(huà),程序就會(huì)崩潰。
  • 這個(gè)方法的實(shí)現(xiàn):

    - (void)setValue:(id)value forUndefinedKey:(NSString *)key { if ([key isEqualToString:@"id"]) {self.uid = value; } }

    案例六:動(dòng)態(tài)變量控制

    在程序中,xiaoming的age是10,后來(lái)被runtime變成了20,來(lái)看看runtime是怎么做到的。

    • 1.動(dòng)態(tài)獲取XiaoMing類(lèi)中的所有屬性[當(dāng)然包括私有]

      `Ivar *ivar = class_copyIvarList([self.xiaoming class], &count);`
    • 2.遍歷屬性找到對(duì)應(yīng)name字段

      `const char *varName = ivar_getName(var);`
    • 3.修改對(duì)應(yīng)的字段值成20

      `object_setIvar(self.xiaoMing, var, @"20");`
    • 4.代碼參考

      -(void)answer{unsigned int count = 0;Ivar ivar = class_copyIvarList([self.xiaoMing class], &count);for (int i = 0; i<count; i++) { Ivar var = ivar[i]; const char varName = ivar_getName(var); NSString *name = [NSString stringWithUTF8String:varName]; if ([name isEqualToString:@”_age”]) { object_setIvar(self.xiaoMing, var, @”20”); break; } } NSLog(@”XiaoMing’s age is %@”,self.xiaoMing.age); }

    案例七:動(dòng)態(tài)添加方法

    在程序當(dāng)中,假設(shè)XiaoMing的中沒(méi)有g(shù)uess這個(gè)方法,后來(lái)被Runtime添加一個(gè)名字叫g(shù)uess的方法,最終再調(diào)用guess方法做出相應(yīng)。那么,Runtime是如何做到的呢?

    1.動(dòng)態(tài)給XiaoMing類(lèi)中添加guess方法:

    class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@: ");

    這里參數(shù)地方說(shuō)明一下:

    (IMP)guessAnswer 意思是guessAnswer的地址指針;
    "v@:" 意思是,v代表無(wú)返回值void,如果是i則代表int;@代表 id sel; : 代表 SEL _cmd;
    “v@:@@” 意思是,兩個(gè)參數(shù)的沒(méi)有返回值。

    2.調(diào)用guess方法響應(yīng)事件:

    [self.xiaoMing performSelectorselector(guess)];

    3.編寫(xiě)guessAnswer的實(shí)現(xiàn):

    void guessAnswer(id self,SEL _cmd){NSLog(@"i am from beijing");}

    這個(gè)有兩個(gè)地方留意一下:

    • void的前面沒(méi)有+、-號(hào),因?yàn)橹皇荂的代碼。
    • 必須有兩個(gè)指定參數(shù)(id self,SEL _cmd)。

    代碼參考:

    -(void)answer{ class_addMethod([self.xiaoMing class], @selector(guess), (IMP)guessAnswer, "v@:");if ([self.xiaoMing respondsToSelector:@selector(guess)]) {[self.xiaoMing performSelector:@selector(guess)];} else{NSLog(@"Sorry,I don't know");}}void guessAnswer(id self,SEL _cmd){NSLog(@"i am from beijing");}

    案例八:在方法上增加額外功能

    有這樣一個(gè)場(chǎng)景,出于某些需求,我們需要跟蹤記錄APP中按鈕的點(diǎn)擊次數(shù)和頻率等數(shù)據(jù),怎么解決?當(dāng)然通過(guò)繼承按鈕類(lèi)或者通過(guò)類(lèi)別實(shí)現(xiàn)是一個(gè)辦法,但是帶來(lái)其他問(wèn)題比如別人不一定會(huì)去實(shí)例化你寫(xiě)的子類(lèi),或者其他類(lèi)別也實(shí)現(xiàn)了點(diǎn)擊方法導(dǎo)致不確定會(huì)調(diào)用哪一個(gè),runtime可以這樣解決:

    @implementation UIButton (Hook)+ (void)load {static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{Class selfClass = [self class];SEL oriSEL = @selector(sendAction:to:forEvent:);Method oriMethod = class_getInstanceMethod(selfClass, oriSEL);SEL cusSEL = @selector(mySendAction:to:forEvent:);Method cusMethod = class_getInstanceMethod(selfClass, cusSEL);BOOL addSucc = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));if (addSucc) {class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));}else {method_exchangeImplementations(oriMethod, cusMethod);}});}- (void)mySendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event { [CountTool addClickCount]; [self mySendAction:action to:target forEvent:event]; }@end

    load方法會(huì)在類(lèi)第一次加載的時(shí)候被調(diào)用,調(diào)用的時(shí)間比較靠前,適合在這個(gè)方法里做方法交換,方法交換應(yīng)該被保證,在程序中只會(huì)執(zhí)行一次。

    轉(zhuǎn)載于:https://www.cnblogs.com/zhuyaguang/p/5660247.html

    總結(jié)

    以上是生活随笔為你收集整理的Runtime 总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。