? Runtime簡(jiǎn)介以及常見(jiàn)的使用場(chǎng)景

Runtime簡(jiǎn)介以及常見(jiàn)的使用場(chǎng)景

? ?Runtime簡(jiǎn)稱運(yùn)行時(shí),是一套比較底層的純C語(yǔ)言的API,作為OC的核心宛乃,運(yùn)行時(shí)是一種面向?qū)ο蟮木幊陶Z(yǔ)言的運(yùn)行環(huán)境归形,其中最主要的是消息機(jī)制,Objective-C就是基于運(yùn)行時(shí)的嘁圈。

? ?所謂運(yùn)行時(shí),是指盡可能地把決定從編譯期推遲到運(yùn)行期,就是盡可能地做到動(dòng)態(tài).只是在運(yùn)行的時(shí)候才會(huì)去確定對(duì)象的類型和方法的.因此利用Runtime機(jī)制可以在程序運(yùn)行時(shí)動(dòng)態(tài)地修改類和對(duì)象中的所有屬性和方法殉农。

? ?對(duì)于C語(yǔ)言刀脏,函數(shù)的調(diào)用在編譯的時(shí)候會(huì)決定調(diào)用哪個(gè)函數(shù)。對(duì)于OC的函數(shù)超凳,屬于動(dòng)態(tài)調(diào)用過(guò)程愈污,在編譯的時(shí)候并不能決定真正調(diào)用哪個(gè)函數(shù)耀态,只有在真正運(yùn)行的時(shí)候才會(huì)根據(jù)函數(shù)的名稱找到對(duì)應(yīng)的函數(shù)來(lái)調(diào)用。

Objective-C從三種不同的層級(jí)上與Runtime系統(tǒng)進(jìn)行交互暂雹,分別是

①通過(guò)Objective-C源代碼首装;

②通過(guò)Foundation框架的NSObject類定義的方法;

③通過(guò)對(duì)Runtime函數(shù)的直接調(diào)用(需要導(dǎo)入#import )杭跪;

? ?大部分情況下只管寫OC代碼就行仙逻,因?yàn)镺C底層默認(rèn)實(shí)現(xiàn)Runtime,每一個(gè)OC的方法涧尿,底層必然有一個(gè)與之對(duì)應(yīng)的Runtime方法系奉。。

以下是Runtime的一些使用場(chǎng)景:

1.發(fā)送消息

方法調(diào)用的本質(zhì)姑廉,就是讓對(duì)象發(fā)送消息缺亮。objc_msgSend,只有對(duì)象才能發(fā)送消息

舉個(gè)簡(jiǎn)單的例子:如下

//(1).調(diào)用對(duì)象方法:

//創(chuàng)建Dog對(duì)象

Dog *dog = [[Dog alloc] init];

//調(diào)用對(duì)象方法

[dog run];

//調(diào)用對(duì)象本質(zhì):讓對(duì)象發(fā)送消息

objc_msgSend(dog, @selector(run));

//(2).調(diào)用類方法方式:

//有兩種

//第一種通過(guò)類名調(diào)用

[Dog run];

//第二種通過(guò)類對(duì)象調(diào)用

[[Dog class] run];

//用類名調(diào)用類方法,底層會(huì)自動(dòng)把類名轉(zhuǎn)換成類對(duì)象調(diào)用

//調(diào)用類方法本質(zhì):讓類對(duì)象發(fā)送消息

objc_msgSend([Dog class],@selector(run));

消息機(jī)制原理:對(duì)象根據(jù)方法編號(hào)SEL去映射表查找對(duì)應(yīng)的方法實(shí)現(xiàn)

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

開(kāi)發(fā)使用場(chǎng)景:如果一個(gè)類方法非常多桥言,加載類到內(nèi)存的時(shí)候也比較耗費(fèi)資源瞬内,需要給每個(gè)方法生成映射表,可以使用動(dòng)態(tài)給某個(gè)類限书,添加方法解決。經(jīng)典面試題:有沒(méi)有使用performSelector章咧,其實(shí)主要想問(wèn)你有沒(méi)有動(dòng)態(tài)添加過(guò)方法倦西。簡(jiǎn)單使用

@implementation ViewController

-(void)viewDidLoad {

[super viewDidLoad];

Person *p = [[Person alloc] init];

//默認(rèn)person,沒(méi)有實(shí)現(xiàn)eat方法赁严,可以通過(guò)performSelector調(diào)用扰柠,但是會(huì)報(bào)錯(cuò)。

//動(dòng)態(tài)添加方法就不會(huì)報(bào)錯(cuò)

[p performSelector:@selector(eat)];

}

@end

@implementation Person

//void(*)()

//默認(rèn)方法都有兩個(gè)隱式參數(shù)疼约,

void eat(id self,SEL sel)

{

NSLog(@"%@%@",self,NSStringFromSelector(sel));

}

//當(dāng)一個(gè)對(duì)象調(diào)用未實(shí)現(xiàn)的方法卤档,會(huì)調(diào)用這個(gè)方法處理,并且會(huì)把對(duì)應(yīng)的方法列表傳過(guò)來(lái).

//剛好可以用來(lái)判斷,未實(shí)現(xiàn)的方法是不是我們想要?jiǎng)討B(tài)添加的方法

+(BOOL)resolveInstanceMethod:(SEL)sel

{ ?if (sel == @selector(eat)) {

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

/**第一個(gè)參數(shù):給哪個(gè)類添加方法程剥,第二個(gè)參數(shù):添加方法的方法編號(hào)劝枣,第三個(gè)參數(shù):添加方法的函數(shù)實(shí)現(xiàn)(函數(shù)地址),第四個(gè)參數(shù):函數(shù)的類型织鲸,(返回值+參數(shù)類型) v:void @:對(duì)象->self :表示SEL->_cmd*/

class_addMethod(self, @selector(eat),eat, "v@:"); ?}

return [super resolveInstanceMethod:sel];

}

@end

注意:

? ?+ (BOOL)resolveInstanceMethod:(SEL)aSEL這個(gè)函數(shù)與forwardingTargetForSelector類似舔腾,都會(huì)在對(duì)象不能接受某個(gè)selector時(shí)觸發(fā),執(zhí)行起來(lái)略有差別搂擦。前者的目的主要在于給用戶一個(gè)機(jī)會(huì)來(lái)向該對(duì)象添加所需的selector稳诚,后者的目的在于允許用戶將selector轉(zhuǎn)發(fā)給另一個(gè)對(duì)象。另外觸發(fā)時(shí)機(jī)也不完全一樣瀑踢,該函數(shù)是個(gè)類函數(shù)扳还,在程序剛啟動(dòng)才避,界面尚未顯示出時(shí),就會(huì)被調(diào)用氨距。

? ?在類不能處理某個(gè)selector的情況下桑逝,如果類重載了該函數(shù),并使用class_addMethod添加了相應(yīng)的selector衔蹲,并返回YES肢娘,那么后面forwardingTargetForSelector就不會(huì)被調(diào)用,如果在該函數(shù)中沒(méi)有添加相應(yīng)的selector舆驶,那么不管返回什么橱健,后面都會(huì)繼續(xù)調(diào)用forwardingTargetForSelector,如果在forwardingTargetForSelector并未返回能接受該selector的對(duì)象沙廉,那么resolveInstanceMethod會(huì)再次被觸發(fā)拘荡,這一次,如果仍然不添加selector撬陵,程序就會(huì)報(bào)異常

3.運(yùn)行時(shí)關(guān)聯(lián)對(duì)象提高效率珊皿,給分類添加屬性。

使用的時(shí)候與懶加載的特點(diǎn)相似巨税,從`關(guān)聯(lián)對(duì)象`中獲取對(duì)象屬性蟋定,如果有,直接返回草添。

@implementation ViewController

-(void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loadingthe view, typically from a nib.

//給系統(tǒng)NSObject類動(dòng)態(tài)添加屬性name

NSObject *objc = [[NSObject alloc] init];

objc.name = @"旺財(cái)";

NSLog(@"%@",objc.name);

}

@end

//定義關(guān)聯(lián)的key

staticconst char *key = "name";

@implementation NSObject (Property)

-(NSString *)name

{

//根據(jù)關(guān)聯(lián)的key驶兜,獲取關(guān)聯(lián)的值。

NSString*name =objc_getAssociatedObject(self, key);

if(name! = nil) {

return name;

}}

-(void)setName:(NSString *)name

{//第一個(gè)參數(shù):給哪個(gè)對(duì)象添加關(guān)聯(lián)

//第二個(gè)參數(shù):關(guān)聯(lián)的key远寸,通過(guò)這個(gè)key獲取

//第三個(gè)參數(shù):關(guān)聯(lián)的value

//第四個(gè)參數(shù):關(guān)聯(lián)的策略

objc_setAssociatedObject(self, key, name,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

@end

4.使用運(yùn)行時(shí)字典轉(zhuǎn)模型

大體思路:利用運(yùn)行時(shí)抄淑,遍歷模型中所有屬性,根據(jù)模型的屬性名驰后,去字典中查找key肆资,取出對(duì)應(yīng)的值,給模型的屬性賦值灶芝。步驟:提供一個(gè)NSObject分類郑原,專門字典轉(zhuǎn)模型,以后所有模型都可以通過(guò)這個(gè)分類轉(zhuǎn)夜涕。(所有字典轉(zhuǎn)模型框架的核心算法)

創(chuàng)建NSObject的分類Runtime:


在.h中的類方法如下

#import

@interface NSObject (Runtime)

///給定一個(gè)字典颤专,創(chuàng)建self類對(duì)應(yīng)的對(duì)象

///@param dict字典

///@return對(duì)象

+ (instancetype)hd_objWithDict:(NSDictionary*)dict;

///獲取類的屬性列表數(shù)組


///@return類的屬性列表數(shù)組

+ (NSArray*)hd_objProperties;

@end

在.m中的類方法如下:

//所有字典轉(zhuǎn)模型框架的核心算法

+ (instancetype)hd_objWithDict:(NSDictionary*)dict {

//實(shí)例化對(duì)象

id object = [[self alloc]init];

//使用字典,設(shè)置對(duì)象信息

1>獲得self的屬性列表

NSArray *proList = [self hd_objProperties];

2>遍歷字典

[dictenumerateKeysAndObjectsUsingBlock:^(id_Nonnullkey,id_Nonnullobj,BOOL*_Nonnullstop) {

NSLog(@"key %@ --- value %@", key,obj);

//

3>判斷key是否在proList中

if([proListcontainsObject:key]) {

//說(shuō)明屬性存在钠乏,可以使用`KVC`設(shè)置數(shù)值

[objectsetValue:objforKey:key];

}}];

return object;

}

constchar* kPropertiesListKey ="CZPropertiesListKey";

+ (NSArray*)hd_objProperties{

//從`關(guān)聯(lián)對(duì)象`中獲取對(duì)象屬性栖秕,如果有,直接返回晓避!

//獲取關(guān)聯(lián)對(duì)象-動(dòng)態(tài)添加的屬性

NSArray*ptyList =objc_getAssociatedObject(self,kPropertiesListKey);

if(ptyList != nil) {

return ptyList;

}

//調(diào)用運(yùn)行時(shí)方法簇捍,取得類的屬性列表

//Ivar成員變量

//Method方法

//Property屬性

//Protocol協(xié)議

//所有屬性的`數(shù)組`只壳,C語(yǔ)言中,數(shù)組的名字暑塑,就是指向第一個(gè)元素的地址

//retain/create/copy需要release吼句,最好option + click

unsigned int count =0;

objc_property_t *proList =class_copyPropertyList([self class], &count);

NSLog(@"屬性的數(shù)量%d", count);

//創(chuàng)建數(shù)組

NSMutableArray *arrayM = [NSMutableArrayarray];

//遍歷所有的屬性

for(unsignedinti = 0; i < count; i++) {

// 1.從數(shù)組中取得屬性

//C語(yǔ)言的結(jié)構(gòu)體指針,通常不需要`*`

objc_property_t ?pty = proList[i];

// 2.從pty中獲得屬性的名稱

const char *cName =property_getName(pty);

NSString *name = [NSStringstringWithCString:cNameencoding:NSUTF8StringEncoding];

//NSLog(@"%@",name);

// 3.屬性名稱添加到數(shù)組

[arrayM addObject:name];

}

//釋放數(shù)組

free(proList);

// ---

2.對(duì)象的屬性數(shù)組已經(jīng)獲取完畢事格,利用關(guān)聯(lián)對(duì)象惕艳,動(dòng)態(tài)添加屬性

/**

參數(shù)

1.對(duì)象self [OC中class也是一個(gè)特殊的對(duì)象]

2.動(dòng)態(tài)添加屬性的key,獲取值的時(shí)候使用

3.動(dòng)態(tài)添加的屬性值

4.對(duì)象的引用關(guān)系

*/

objc_setAssociatedObject(self,kPropertiesListKey, arrayM.copy,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

return arrayM.copy;

}

注意:必須保證驹愚,模型中的屬性和字典中的key一一對(duì)應(yīng)远搪。如果不一致,就會(huì)調(diào)用[ setValue:forUndefinedKey:]逢捺,報(bào)key找不到的錯(cuò)谁鳍。

分析:模型中的屬性和字典的key不一一對(duì)應(yīng),系統(tǒng)就會(huì)調(diào)用setValue:forUndefinedKey:報(bào)錯(cuò)劫瞳。

解決:重寫對(duì)象的setValue:forUndefinedKey:,把系統(tǒng)的方法覆蓋倘潜,

就能繼續(xù)使用KVC,字典轉(zhuǎn)模型了志于。

-(void)setValue:(id)value forUndefinedKey:(NSString *)key

{

}

? ?通過(guò)運(yùn)行時(shí)字典轉(zhuǎn)模型的好處在于寫在NSObject的分類中涮因,和類的關(guān)聯(lián)性不強(qiáng)對(duì)類解耦,以后再做字典轉(zhuǎn)模型的時(shí)候只需要把這個(gè)分類往任何一個(gè)程序中一拖伺绽,程序中的對(duì)象就都具備了這個(gè)字典轉(zhuǎn)模型的方法蕊退。

5.交叉方法(黑魔法)

? ?開(kāi)發(fā)使用場(chǎng)景:系統(tǒng)自帶的方法功能不夠,給系統(tǒng)自帶的方法擴(kuò)展一些功能憔恳,并且保持原有的功能。方式一:繼承系統(tǒng)的類净蚤,重寫方法.方式二:使用runtime,交換方法.

? ?Runtime在AFN中的使用細(xì)節(jié):在AFN的NSURLSessionMangerM方法里面第363行寫了一個(gè)靜態(tài)的內(nèi)聯(lián)函數(shù)钥组,做了一個(gè)交叉方法,交叉的是af_resume和resume方法今瀑,這樣的話程梦,可以在發(fā)送網(wǎng)絡(luò)之前發(fā)起一個(gè)通知,能接受到任何一個(gè)網(wǎng)絡(luò)請(qǐng)求的事件的變化橘荠。

@implementation ViewController

-(void)viewDidLoad {

[super viewDidLoad];

// Do any additional setup after loadingthe view, typically from a nib.

//需求:給imageNamed方法提供功能屿附,每次加載圖片就判斷下圖片是否加載成功。

//步驟一:先搞個(gè)分類哥童,定義一個(gè)能加載圖片并且能打印的方法+ (instancetype)imageWithName:(NSString *)name;

//步驟二:交換imageNamed和imageWithName的實(shí)現(xiàn)挺份,就能調(diào)用imageWithName,間接調(diào)用imageWithName的實(shí)現(xiàn)贮懈。

UIImage *image = [UIImageimageNamed:@"123"];

}

@end

@implementation UIImage (Image)

//加載分類到內(nèi)存的時(shí)候調(diào)用

+(void)load

{

//交換方法

//獲取imageWithName方法地址

Method imageWithName =class_getClassMethod(self, @selector(imageWithName:));

//獲取imageWithName方法地址

Method imageName =class_getClassMethod(self, @selector(imageNamed:));

//交換方法地址匀泊,相當(dāng)于交換實(shí)現(xiàn)方式

method_exchangeImplementations(imageWithName,imageName);

}

//既能加載圖片又能打印

+(instancetype)imageWithName:(NSString *)name

{//這里調(diào)用imageWithName优训,相當(dāng)于調(diào)用imageName

UIImage *image = [self imageWithName:name];

if (image == nil) {

NSLog(@"加載空的圖片");}

return image;

}

@end

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市各聘,隨后出現(xiàn)的幾起案子揣非,更是在濱河造成了極大的恐慌,老刑警劉巖躲因,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件早敬,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡大脉,警方通過(guò)查閱死者的電腦和手機(jī)搞监,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)箱靴,“玉大人腺逛,你說(shuō)我怎么就攤上這事『饣常” “怎么了棍矛?”我有些...
    開(kāi)封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)抛杨。 經(jīng)常有香客問(wèn)我够委,道長(zhǎng),這世上最難降的妖魔是什么怖现? 我笑而不...
    開(kāi)封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任茁帽,我火速辦了婚禮,結(jié)果婚禮上屈嗤,老公的妹妹穿的比我還像新娘潘拨。我一直安慰自己,他們只是感情好饶号,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布铁追。 她就那樣靜靜地躺著,像睡著了一般茫船。 火紅的嫁衣襯著肌膚如雪琅束。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天算谈,我揣著相機(jī)與錄音涩禀,去河邊找鬼。 笑死然眼,一個(gè)胖子當(dāng)著我的面吹牛艾船,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼丽声,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼礁蔗!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起雁社,我...
    開(kāi)封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤浴井,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后霉撵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磺浙,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年徒坡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撕氧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡喇完,死狀恐怖伦泥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情锦溪,我是刑警寧澤不脯,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站刻诊,受9級(jí)特大地震影響防楷,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜则涯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一复局、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧粟判,春花似錦亿昏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至事秀,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間野舶,已是汗流浹背易迹。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留平道,地道東北人睹欲。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親窘疮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子袋哼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,690評(píng)論 0 9
  • 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的轉(zhuǎn)載 這篇文章完全是基于南峰子老師博客的...
    西木閱讀 30,548評(píng)論 33 466
  • 對(duì)于從事 iOS 開(kāi)發(fā)人員來(lái)說(shuō)闸衫,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,700評(píng)論 7 64
  • 黑桃先生讓我不要洗衣服了 黑桃先生自己燒水 黑桃先生問(wèn)我要不要熱水袋 黑桃先生說(shuō)把空調(diào)打開(kāi)涛贯,他去端盆水到房間 黑桃...
    大圣快來(lái)閱讀 344評(píng)論 0 0
  • 燈光獻(xiàn)給了城市 我的憂慮就種下了病情 黑夜蓋足了被子 我卻迷失了垂聽(tīng)的聲音 黎明喚醒了黑暗 后背再一次冰涼 治愈,...
    零溫度閱讀 173評(píng)論 0 4