Runtime--Dynamic Method Resolution
很多時候你想動態(tài)的提供方法的實(shí)現(xiàn)肩豁,比如說聲明property的時候使用編譯器指令@dynamic
@dynamic propertyName;
可以通過實(shí)現(xiàn)以下兩個方法達(dá)到動態(tài)實(shí)現(xiàn)方法的目的
//如果找到方法實(shí)現(xiàn)并且添加到Class脊串,則返回Yes,否則NO
//針對類方法
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
//如果找到方法實(shí)現(xiàn)并且添加到Class清钥,則返回Yes琼锋,否則NO
//針對實(shí)例方法
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
為了模擬場景,首先在新建工程的ViewController中聲明但是不實(shí)現(xiàn)兩個方法
@interface ViewController ()
-(void)ivarMethod;
+(void)classMethod;
@end
//此處會收到警告提示方法未實(shí)現(xiàn)
@implementation ViewController
然后定義兩個對應(yīng)的函數(shù)循捺,Objective-C方法簡單來說就是至少帶有兩個參數(shù)的C函數(shù)斩例,這兩個參數(shù)為self
and _cmd
雄人,代碼如下
void ivar(id self,SEL _cmd)
{
NSLog(@"調(diào)用ivar");
}
void class(id self,SEL _cmd)
{
NSLog(@"調(diào)用class");
}
然后利用Runtime的class_addMethod
函數(shù)給方法添加對應(yīng)的implementation
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
if (sel == @selector(ivarMethod))
{
bool success = class_addMethod([self class], sel, (IMP)ivar, "v@:");
return success;
}
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel
{
if (sel == @selector(classMethod))
{
//此二者均可:object_getClass(self)从橘、objc_getMetaClass("ViewController")
bool success = class_addMethod(object_getClass(self), sel, (IMP)class, "v@:");
return success;
}
return [super resolveClassMethod:sel];
}
最好調(diào)用方法完成動態(tài)添加實(shí)現(xiàn)
- (void)viewDidLoad
{
[super viewDidLoad];
[self ivarMethod];
[ViewController classMethod];
}
//輸出結(jié)果
調(diào)用ivar
調(diào)用class
看到輸出結(jié)果表示動態(tài)添加效果已實(shí)現(xiàn)。
Message Forwarding 和 Dynamic method resolution
后者發(fā)生在前者之前础钠。如果已經(jīng)實(shí)現(xiàn) resolveInstanceMethod:
又想進(jìn)行消息轉(zhuǎn)發(fā)恰力,那么這些方法都要返回NO(不要給方法添加實(shí)現(xiàn))。下面通過代碼來驗(yàn)證一下旗吁。
首先在以上代碼的基礎(chǔ)上重寫消息轉(zhuǎn)發(fā)必要的兩個方法
//Data類中方法實(shí)現(xiàn)
-(void)ivarMethod
{
NSLog(@"調(diào)用ivarMethod");
}
/***********分割線***********/
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString *sel = NSStringFromSelector(anInvocation.selector);
if ([sel isEqualToString:@"ivarMethod"])
{
//消息轉(zhuǎn)發(fā)到Data中
[anInvocation invokeWithTarget:[[Data alloc] init]];
}
else
{
[super forwardInvocation:anInvocation];
}
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature *signature = [super methodSignatureForSelector:selector];
//生成方法簽名
if (! signature)
{
//處理不同的方法
if (sel_isEqual(selector, @selector(ivarMethod)))
{
signature = [[[Data alloc] init] methodSignatureForSelector:selector];
}
}
return signature;
}
然后resolveInstanceMethod:沒有添加動態(tài)實(shí)現(xiàn)才能進(jìn)行消息轉(zhuǎn)發(fā)踩萎,否則直接執(zhí)行了
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
NSLog(@"resolveInstanceMethod");
return [super resolveInstanceMethod:sel];
}
調(diào)用方法,通過打印結(jié)果順序驗(yàn)證結(jié)果
- (void)viewDidLoad {
[super viewDidLoad];
[self ivarMethod];
}
//輸出結(jié)果
resolveInstanceMethod
調(diào)用ivarMethod
forwardingTargetForSelector:
- (id)forwardingTargetForSelector:(SEL)aSelector OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0);
1很钓、當(dāng)有unrecognized消息時香府,返回一個對象,以便消息可以轉(zhuǎn)發(fā)码倦。
2企孩、如果一個對象繼承或者實(shí)現(xiàn)了此方法,而且返回此對象袁稽,那么消息被成功轉(zhuǎn)發(fā)調(diào)用
3勿璃、禁止返回self對象,否則引起死循環(huán)
4、對于不處理的方法补疑,返回super’s implementation的結(jié)果
5歧沪、調(diào)用發(fā)生在forwardInvocation:
之前;轉(zhuǎn)發(fā)速度更快
6莲组、不能夠像NSInvocation那樣操作返回值和參數(shù)
下邊通過代碼展示簡單用法和驗(yàn)證執(zhí)行順序,在以上代碼的基礎(chǔ)上
- (id)forwardingTargetForSelector:(SEL)aSelector
{
NSLog(@"forwardingTargetForSelector");
if (aSelector == @selector(ivarMethod))
{
return [[Data alloc] init];
}
else
{
return [super forwardingTargetForSelector:aSelector];
}
}
/***********分割線***********/
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
NSString *sel = NSStringFromSelector(anInvocation.selector);
//方法對比打印次序
NSLog(@"forwardInvocation");
if ([sel isEqualToString:@"ivarMethod"])
{
[anInvocation invokeWithTarget:[[Data alloc] init]];
}
else
{
[super forwardInvocation:anInvocation];
}
}
調(diào)用變查看輸出結(jié)果
- (void)viewDidLoad {
[super viewDidLoad];
[self ivarMethod];
}
//輸出結(jié)果
resolveInstanceMethod //Dynamic Method Resolution機(jī)制
forwardingTargetForSelector //消息轉(zhuǎn)發(fā)
調(diào)用ivarMethod //方法調(diào)用
通過結(jié)果可以看出處理 unrecognized messages的順序:
1诊胞、Dynamic Method Resolution機(jī)制
2、- (void)forwardInvocation:(NSInvocation *)anInvocation
3胁编、Message Forwarding
參考文獻(xiàn):Dynamic Method Resolution厢钧、NSObject、forwardingTargetForSelector:嬉橙、Message Forwarding