初識(shí)RunTime

初次接觸RunTime,記錄下自己的學(xué)習(xí)心得,為后來(lái)者鋪平道路,提供一個(gè)學(xué)習(xí)的切入點(diǎn)沟饥。

首先簡(jiǎn)單的介紹下RunTime,它就是傳說(shuō)中的運(yùn)行時(shí)迅涮,為什么是傳說(shuō)中的呢废赞?因?yàn)槲覀兪状温牭竭\(yùn)行時(shí)大多都是從他人口中,只知道很底層的東西叮姑,非常難以理解唉地。好了,上面都是廢話传透,我們先要了解蘋果的Object-C是面向運(yùn)行時(shí)的語(yǔ)言耘沼,是動(dòng)態(tài)的,它把編譯和鏈接期所做的事推遲到運(yùn)行時(shí)才進(jìn)行處理朱盐,給了我們很大的靈活性群嗤,可以進(jìn)行消息的轉(zhuǎn)發(fā),替換方法等兵琳。

理論上的東西說(shuō)的再多狂秘,不會(huì)用都是瞎掰骇径,我相信用的多了,慢慢自己也會(huì)深究其理者春,自然也就懂了破衔。那么RunTime可以用來(lái)做什么呢?

一钱烟、動(dòng)態(tài)添加方法的實(shí)現(xiàn)

先簡(jiǎn)單了解一下方法的調(diào)用機(jī)制晰筛,以Person類為例:

以對(duì)象方法為例當(dāng)調(diào)用eat方法時(shí)[p eat],底層會(huì)調(diào)用[p performSelector:@selector(eat)]方法忠售,編譯器再將代碼轉(zhuǎn)化為objc_msgSend(p, @selector(eat));

當(dāng)使用類方法調(diào)用時(shí)传惠,eat為類方法,[Person eat]會(huì)轉(zhuǎn)化為[[Person class] performSelector:@selector(eat)]方法稻扬,然后編譯器再將代碼轉(zhuǎn)化為objc_msgSend(p, @selector(eat));

那么當(dāng)我們只是對(duì)方法進(jìn)行了聲明卦方,而沒有實(shí)現(xiàn)時(shí),編譯時(shí)會(huì)有警告而不會(huì)報(bào)錯(cuò)泰佳。我們都知道在運(yùn)行時(shí)系統(tǒng)找不到相應(yīng)的方法實(shí)現(xiàn)盼砍,程序就會(huì)崩潰。但系統(tǒng)給了我們機(jī)會(huì)來(lái)防止崩潰逝她,在崩潰之前系統(tǒng)會(huì)來(lái)到攔截調(diào)用浇坐。攔截調(diào)用就是系統(tǒng)在找不到調(diào)用的方法,程序崩潰之前調(diào)用的方法黔宛。

當(dāng)調(diào)用了沒有實(shí)現(xiàn)的對(duì)象方法時(shí)近刘,就會(huì)調(diào)用+(BOOL)resolveInstanceMethod:(SEL)sel方法。當(dāng)調(diào)用了沒有實(shí)現(xiàn)的類方法時(shí)臀晃,就會(huì)調(diào)用+(BOOL)resolveClassMethod:(SEL)sel方法觉渴。通過這兩個(gè)方法就可以知道哪些方法沒有實(shí)現(xiàn),從而動(dòng)態(tài)添加方法徽惋。參數(shù)sel即表示沒有實(shí)現(xiàn)的方法案淋。

一個(gè)objective - C方法最終都是一個(gè)C函數(shù),默認(rèn)任何一個(gè)方法都有兩個(gè)參數(shù)险绘。self : 方法調(diào)用者 _cmd : 調(diào)用方法編號(hào)踢京。我們可以使用函數(shù)class_addMethod為類添加一個(gè)方法以及實(shí)現(xiàn)。

代碼實(shí)現(xiàn):需要在Person類的實(shí)現(xiàn)方法中調(diào)用

+(BOOL)resolveInstanceMethod:(SEL)sel{

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

// 首先判斷sel是不是eat方法 也可以轉(zhuǎn)化成字符串進(jìn)行比較宦棺。

if ([NSStringFromSelector(sel) isEqualToString:@"eat"]) {

/**

來(lái)看一下class_addMethod(__unsafe_unretained Class cls, SEL name, IMP imp, const char *types) 的參數(shù):

第一個(gè)參數(shù): cls:給哪個(gè)類添加方法

第二個(gè)參數(shù): SEL name:添加方法的編號(hào)

第三個(gè)參數(shù): IMP imp: 方法的實(shí)現(xiàn)瓣距,函數(shù)入口,函數(shù)名可與方法名不同(建議與方法名相同)

第四個(gè)參數(shù): types :方法類型渺氧,需要用特定符號(hào)旨涝,參考API

示例:v -> void 表示無(wú)返回值, @ -> object 表示id參數(shù),: -> method selector 表示SEL

*/

class_addMethod(self, sel, (IMP)eat , "v@:");

// 處理完返回YES

return YES;

}

return [super resolveInstanceMethod:sel];

}

?void eat (id self ,SEL _cmd){// 實(shí)現(xiàn)內(nèi)容

NSLog(@"%@的%@方法動(dòng)態(tài)實(shí)現(xiàn)了",self,NSStringFromSelector(_cmd));

}

二白华、動(dòng)態(tài)為分類添加屬性

RunTime提供了動(dòng)態(tài)添加屬性和獲得屬性的方法:

設(shè)置關(guān)聯(lián)屬性的值:

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); ?參數(shù)介紹:參數(shù)1 ? 為哪個(gè)對(duì)象關(guān)聯(lián)屬性?

? ? ? ? ? ? ? ? ? ?參數(shù)2 ?可以自定義key值慨默,用來(lái)獲取被關(guān)聯(lián)屬性的值

? ? ? ? ? ? ? ? ? 參數(shù)3 ? 關(guān)聯(lián)的值,也就是set方法傳入的值給屬性去保存弧腥。

? ? ? ? ? ? ? ? ? 參數(shù)4 ? 屬性的保存方式厦取,常用OBJC_ASSOCIATION_RETAIN_NONATOMIC

獲取關(guān)聯(lián)屬性的值:

objc_getAssociatedObject(id object, const void *key);

參數(shù)介紹:參數(shù)1 ? 為哪個(gè)對(duì)象關(guān)聯(lián)屬性

? ? ? ? ? ? ? ? ? ?參數(shù)2 ? 通過自定義的key值獲取被關(guān)聯(lián)屬性的值

示例:為UIImageview的分類添加屬性@property(copy,nonatomic)NSString *imageName;

// 給分類添加屬性

- (void)setImageName:(NSString *)imageName {

//使用關(guān)聯(lián)對(duì)象設(shè)置值

objc_setAssociatedObject(self, @"name", imageName, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}

- (NSString *)imageName {

//獲取關(guān)聯(lián)對(duì)象設(shè)置的值

NSString *imgName = objc_getAssociatedObject(self, @"name");

return imgName;

}

三、動(dòng)態(tài)獲取一個(gè)類中的成員變量管搪、屬性虾攻、方法、協(xié)議

需要?jiǎng)?chuàng)建NSObject的分類更鲁,這樣就可以獲取所有類的員變量霎箍、屬性、方法澡为、協(xié)議漂坏,并且與KVC配合可以修改系統(tǒng)的類

1)獲取屬性列表

const void * kAssociateObjectKey = @"kAssociateObjectKey";// key值用來(lái)做標(biāo)識(shí)的

+ (NSArray *)yk_propertyArray

{//關(guān)聯(lián)屬性

NSArray *prolist = objc_getAssociatedObject(self, kAssociateObjectKey);

if (prolist != nil) {

return prolist;

}

unsigned int count ;

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

//臨時(shí)可變數(shù)組

NSMutableArray *arrM = [NSMutableArray array];

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

? ? ?//取出屬性

? ? ?objc_property_t proterty =? protertylist[i];

? ? ? //獲取屬性名

? ? ? const char *name = property_getName(proterty);

? ? ? NSString *nameStr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

? ? ? ?[arrM addObject:nameStr];

? ? ? }

// 設(shè)置關(guān)聯(lián)屬性

objc_setAssociatedObject(self, kAssociateObjectKey, arrM.copy,OBJC_ASSOCIATION_RETAIN_NONATOMIC);

//釋放內(nèi)存

free(protertylist);

return arrM.copy;

}

2)獲取成員變量列表

// 獲取成員變量

+(NSArray *)yk_ivarArray

{

unsigned int count;

Ivar *ivarList = class_copyIvarList([self class], &count);

NSMutableArray *arr = [NSMutableArray array];

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

? ? ? // 取出成員變量

? ? ? Ivar ivar = ivarList[i];

? ? ?// 獲取成員變量名

? ? ? const char *name = ivar_getName(ivar);

? ? ?// 將C語(yǔ)言字符串轉(zhuǎn)化為OC字符串

? ? ?NSString *ivarStr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];

? ? ? NSLog(@"ivar = %@",ivarStr);

? ? ? [arr addObject:ivarStr];

? ? ? }

free(ivarList);

//? ? NSLog(@"%zd",count);

return arr.copy;

}

3)獲取方法列表

//獲取方法名

+ (NSArray *)yk_methodList{

unsigned int count;

Method *list = class_copyMethodList([self class], &count);

NSMutableArray *arr = [NSMutableArray array];

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

? ? ? ? // 獲取方法結(jié)構(gòu)體

? ? ? ?Method m = list[i];

? ? ? ?// 獲取方法名

? ? ? ?SEL selector = method_getName(m);

? ? ? ? // 轉(zhuǎn)化為字符串

? ? ? ?NSString *name = NSStringFromSelector(selector);

? ? ? ?[arr addObject:name];

? ? ? ?}

free(list);

return arr.copy;

}

4) 獲取協(xié)議列表

// 獲取協(xié)議名

+ (NSArray *)yk_protocolList{

?unsigned int count;

// 返回值需要標(biāo)記

__unsafe_unretained? Protocol ** proList = class_copyProtocolList([self class], &count);

NSMutableArray *arr = [NSMutableArray array];

//遍歷求協(xié)議名

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

? ? ? ? ?Protocol *pro = proList[i];

? ? ? ? ?const char *name = protocol_getName(pro);

? ? ? ? ? NSString *nameStr = [NSString stringWithCString:name ? ? ? ? ? ? encoding:NSUTF8StringEncoding];

? ? ? ? ? [arr addObject:nameStr];

}

free(proList);

return arr.copy;

}

四、在獲取屬性列表的基礎(chǔ)上實(shí)現(xiàn)簡(jiǎn)單的字典轉(zhuǎn)模型

// 字典數(shù)組轉(zhuǎn)模型 以字典轉(zhuǎn)模型為基礎(chǔ)的

+ (NSArray *)modelWithDictArray:(NSArray *)array

{

if (array.count == 0) {

NSLog(@"數(shù)組為空媒至!");

return nil;

}

NSAssert([array[0] isKindOfClass:[NSDictionary class]], @"數(shù)組內(nèi)容必須為字典");

NSMutableArray *arr = [NSMutableArray array];

for (NSDictionary *dict in array) {

? ? ? ? // 創(chuàng)建模型對(duì)象

? ? ? ? id instance = [self modelWithDict:dict];

? ? ? ?[arr addObject:instance];

? ? ? ? ?}

return arr.copy;

}

// 字典轉(zhuǎn)模型

+(instancetype)modelWithDict:(NSDictionary *)dict

{

id instance = [[self alloc] init];

//獲取屬性列表 不能每次都用調(diào)用獲取屬性列表的方法 效率太低

NSArray *prolist = [self yk_propertyArray];

//遍歷字典

[dict enumerateKeysAndObjectsUsingBlock:^(id? _Nonnull key, id? _Nonnull obj, BOOL * _Nonnull stop) {

// 判斷字典屬性是否在屬性列表中

if ([prolist containsObject:key]) {

[instance setValue:obj forKey:key];

}

}];

return instance;

}

提供GitHub上demo的下載地址:鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末顶别,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子拒啰,更是在濱河造成了極大的恐慌驯绎,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件谋旦,死亡現(xiàn)場(chǎng)離奇詭異剩失,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)册着,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門赴叹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人指蚜,你說(shuō)我怎么就攤上這事≌墙罚” “怎么了摊鸡?”我有些...
    開封第一講書人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)蚕冬。 經(jīng)常有香客問我免猾,道長(zhǎng),這世上最難降的妖魔是什么囤热? 我笑而不...
    開封第一講書人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任猎提,我火速辦了婚禮,結(jié)果婚禮上旁蔼,老公的妹妹穿的比我還像新娘锨苏。我一直安慰自己疙教,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開白布伞租。 她就那樣靜靜地躺著贞谓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪葵诈。 梳的紋絲不亂的頭發(fā)上裸弦,一...
    開封第一講書人閱讀 52,166評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音作喘,去河邊找鬼理疙。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泞坦,可吹牛的內(nèi)容都是我干的窖贤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼暇矫,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼主之!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起李根,我...
    開封第一講書人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤槽奕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后房轿,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體粤攒,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年囱持,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夯接。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡纷妆,死狀恐怖盔几,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情掩幢,我是刑警寧澤逊拍,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站际邻,受9級(jí)特大地震影響芯丧,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜世曾,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一缨恒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦骗露、人聲如沸岭佳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)驼唱。三九已至,卻和暖如春驹暑,著一層夾襖步出監(jiān)牢的瞬間玫恳,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工优俘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留京办,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓帆焕,卻偏偏與公主長(zhǎng)得像惭婿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叶雹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

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

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉财饥,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 1,725評(píng)論 0 9
  • 一.runtime的基本屬性 SEL objc_msgSend函數(shù)第二個(gè)參數(shù)類型為SEL,它是selector在O...
    willphonez閱讀 270評(píng)論 0 3
  • 對(duì)于從事 iOS 開發(fā)人員來(lái)說(shuō)折晦,所有的人都會(huì)答出【runtime 是運(yùn)行時(shí)】什么情況下用runtime?大部分人能...
    夢(mèng)夜繁星閱讀 3,725評(píng)論 7 64
  • 最近發(fā)現(xiàn)一個(gè)有意思的現(xiàn)象钥星,我在評(píng)論某些文章時(shí),總會(huì)被顯示“××贊了你的評(píng)論”满着。 其實(shí)像我這么俗的人谦炒,最想看到的提示...
    酒言醉語(yǔ)閱讀 1,017評(píng)論 38 23
  • 英國(guó)學(xué)者詹尼特·托德總結(jié)了五種女性友誼:浪漫型、欲望型风喇、控制型宁改、政治型、社交型魂莫;并且認(rèn)為还蹲,一般流行文化里呈現(xiàn)的大都...
    鄭州小強(qiáng)閱讀 546評(píng)論 2 2