貓貓分享,必須精品
一:runtime動(dòng)態(tài)添加方法
應(yīng)用場(chǎng)景:
面試中有人會(huì)這么問(wèn):你有用過(guò) performSelector 方法嗎又碌?
貓貓傻傻的說(shuō),用過(guò)呀耽装,這個(gè)還是很好用的期揪,比如我可以延遲幾秒鐘運(yùn)行這個(gè)方法,或者是選擇在哪個(gè)線程中運(yùn)行姓建,不用寫GCD那一大串的代碼速兔,顯得代碼簡(jiǎn)潔明了活玲。谍婉。穗熬。(得意中丁溅,從一般扯到了GCD,快來(lái)夸夸我窟赏,多膩害Q那睢)
好吧,其實(shí)人家是想問(wèn)有沒有用到過(guò)動(dòng)態(tài)添加方法作煌,但是我的一串說(shuō)辭完全給自己封路了蝠嘉,其實(shí)之前對(duì)于runtime有過(guò)了解蚤告,現(xiàn)在呢就仔細(xì)說(shuō)說(shuō)吧:
-
performSelector方法區(qū)別于直接調(diào)用,如果我調(diào)用一個(gè)沒有定義的方法获诈,在編譯時(shí)直接調(diào)用會(huì)給報(bào)錯(cuò)心褐,紅色的哦逗爹。如果用的是performSelector方法,那就只會(huì)有警告(貌似早期木有)挟冠。所以袍睡,人家想問(wèn)得就是,你有沒有用過(guò)動(dòng)態(tài)添加方法控淡,貓給翻譯下就是掺炭,一個(gè)方法我沒有定義,直接調(diào)找不到泥栖,但是我還想運(yùn)行他勋篓,你能辦到嗎譬嚣?(喵你一臉钞它。页响。搀庶。)如下圖
- 我想大家應(yīng)該明白了,其實(shí)這東西跟懶加載差不多小腊,這問(wèn)題也不是說(shuō)面試人吃飽了撐得沒事干弄出來(lái)的久窟,他是有真實(shí)意義存在的(雖然我就認(rèn)為他吃飽撐的)斥扛。當(dāng)硬件內(nèi)存過(guò)小的時(shí)候,如果我們將每個(gè)方法都直接加到內(nèi)存當(dāng)中去芬失,但是幾百年不用一次峻村,這樣就造成了浪費(fèi)粘昨,就跟懶加載一樣窜锯,我方法定義好锚扎,但是只有當(dāng)你用的時(shí)候我猜加載你馁启,這個(gè)呢就必須用到runtime的一些知識(shí)了惯疙。就是performSelector:以及動(dòng)態(tài)添加方法
解決
首先定義一個(gè)類Cat,然后我們假設(shè)貓有個(gè)eat的方法对碌,用performSelector調(diào)用蒿偎。
@interface Cat : NSObject
@end
@implementation Cat
@end
然后如下調(diào)用
就崩了诉位。苍糠。。:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Cat eat:]: unrecognized selector sent to instance 0x7ff1baf08160'
不蹦才怪歹袁,啥都沒有的的空定義類条舔,就繼承了NSObject乏矾,連瞎貓都不算钻心。
- 正經(jīng)的代碼中,當(dāng)performSelector方法調(diào)用某個(gè)sel的時(shí)候摊沉,這時(shí)候會(huì)到調(diào)用對(duì)象的
+ (BOOL)resolveInstanceMethod:(SEL)sel
方法中说墨,如果這里返回是NO,就表示找不到
void eat(id self, SEL _cmd, NSString *param){
NSLog(@"調(diào)用eat 參數(shù):1%@ 參數(shù)2:%@ 參數(shù)3:%@",self,NSStringFromSelector(_cmd),param);
}
@interface Cat : NSObject
@end
@implementation Cat
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(eat:)) {
/**
* 動(dòng)態(tài)添加方法
*
* @param self cls:為哪個(gè)類添加方法
* @param sel SEL:添加方法的方法編號(hào)(方法名)是什么
* @param IMP IMP:方法實(shí)現(xiàn)
* @param const char * types方法類型
*
* @return 返回是否添加成功
*/
BOOL isSuccess = class_addMethod(self, sel, (IMP)eat, "v@:@");
return isSuccess;
}
return [super resolveInstanceMethod:sel];
}
@end
-
用上面的代碼聲明,然后調(diào)用
這時(shí)候就是調(diào)用成功了楼咳。
補(bǔ)充
一: 動(dòng)態(tài)添加方法烛恤,首要要實(shí)現(xiàn)要添加類的resolveInstanceMethod
方法。
-
resolveInstanceMethod
的作用就是當(dāng)調(diào)用了沒有實(shí)現(xiàn)的方法沒有實(shí)現(xiàn)就會(huì)調(diào)用糙申,然后就可以根據(jù)他的參數(shù)sel(參數(shù)sel就是沒有實(shí)現(xiàn)的方法)來(lái)做一系列的操作。 - 此方法定義于NSObject當(dāng)中粱锐,所以說(shuō)每個(gè)類其實(shí)都有他扛邑,只要重寫蔬崩,不過(guò)不要忘了方法后面的
return [super resolveInstanceMethod:sel];
。其實(shí)這些可以看官方文檔的跨琳,雖然貓的英語(yǔ)很爛脉让,但是在有道詞典的幫助下還是能看懂的功炮,我相信你也可以溅潜。
/**
* 當(dāng)調(diào)用了沒有實(shí)現(xiàn)的方法沒有實(shí)現(xiàn)就會(huì)調(diào)用
*
* @param sel 沒有實(shí)現(xiàn)方法
*
* @return 如果方法被發(fā)現(xiàn)并添加return:Yes 否則NO
*/
+ (BOOL)resolveInstanceMethod:(SEL)sel;
二: 對(duì)于自己提前定義的方法eat:來(lái)說(shuō),很明顯這是c語(yǔ)音定義的方法薪伏,與平常方法不同的是他多了兩個(gè)參數(shù)滚澜,其實(shí)OC當(dāng)中每個(gè)方法里面都有這兩個(gè)參數(shù),他們是隱藏參數(shù)嫁怀,一個(gè)代表本身设捐,另一個(gè)代碼的是方法名SEL潦牛,通過(guò)圖調(diào)用成功 那里你可以看到打印的結(jié)果。
/**
* 吃方法
*
* @param self 隱藏參數(shù) self
* @param _cmd 隱藏參數(shù) SEL
* @param param 參數(shù)
*/
void eat(id self, SEL _cmd, NSString *param){
NSLog(@"調(diào)用eat 參數(shù):1%@ 參數(shù)2:%@ 參數(shù)3:%@",self,NSStringFromSelector(_cmd),param);
}
三:動(dòng)態(tài)添加時(shí)用的的方法
/**
* 動(dòng)態(tài)添加方法
*
* @param self cls:為哪個(gè)類添加方法
* @param sel SEL:添加方法的方法編號(hào)(方法名)是什么
* @param IMP IMP:方法實(shí)現(xiàn)
* @param const char * types方法類型
*
* @return 返回是否添加成功
*/
BOOL isSuccess = class_addMethod(self, sel, (IMP)eat, "v@:@");
- 這里的基本東西前文說(shuō)的挺多了挡育,唯一就是最后一個(gè)參數(shù)
@param const char * types方法類型
這個(gè)是沒有見過(guò)的,其是可以通過(guò)查閱官方文檔了解的即寒。
查找方法:看圖按照步驟1橡淆,2,3(貓貓最開始找的時(shí)候就是瞎找母赵,看到像的就點(diǎn)點(diǎn)逸爵,英語(yǔ)是硬傷啊,看代碼還行凹嘲,看英文注釋感覺貓貓要瞎笆蟆)
二:runtime動(dòng)態(tài)添加屬性
應(yīng)用場(chǎng)景
在分類中,所寫的@property (nonatomic, strong) NSString *name;
都僅僅是生成了get和set方法周蹭,并沒有生成對(duì)應(yīng)的_name屬性趋艘,但是有時(shí)候我們會(huì)有一種需求,想要讓分類中保存一下新的屬性值凶朗,因?yàn)閟et和get方法只能是對(duì)已經(jīng)有的東西做操作瓷胧,比如說(shuō)最常用的UIView的分類我們對(duì)frame中的x,y棚愤,width搓萧,height做操作。
解決
用runtime動(dòng)態(tài)的給分類添加屬性宛畦,并且另他產(chǎn)生關(guān)聯(lián)瘸洛,使用如下
- 聲明:
@interface NSObject (Objc)
@property (nonatomic, strong) NSString *name;
@end
- 實(shí)現(xiàn)getSet方法
#import "NSObject+Objc.h"
#import <objc/message.h>
@implementation NSObject (Objc)
- (void)setName:(NSString *)name{
objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSString *)name{
return objc_getAssociatedObject(self, @"name");
}
@end
- 運(yùn)行
#import "ViewController.h"
#import "NSObject+Objc.h"
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
NSObject *objc = [[NSObject alloc] init];
objc.name = @"貓貓";
NSLog(@"%@",objc.name);
}
@end
補(bǔ)充解釋
- runtime中的objc_setAssociatedObject方法
/**
* 根據(jù)某個(gè)對(duì)象,還有key次和,還有對(duì)應(yīng)的策略(copy,strong等) 動(dòng)態(tài)的將值設(shè)置到這個(gè)對(duì)象的key上
*
* @param object 某個(gè)對(duì)象
* @param key 屬性名,根據(jù)key去獲取關(guān)聯(lián)的對(duì)象
* @param value 要設(shè)置的值
* @param policy 策略(copy,strong反肋,assign等)
*/
OBJC_EXPORT void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);
- runtime中的objc_getAssociatedObject方法
/**
* 根據(jù)某個(gè)對(duì)象,還有key 動(dòng)態(tài)的獲取到這個(gè)對(duì)象的key對(duì)應(yīng)的屬性的值
*
* @param object 某個(gè)對(duì)象
* @param key key
*
* @return 對(duì)象的值
*/
OBJC_EXPORT id objc_getAssociatedObject(id object, const void *key)
__OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_3_1);