我们这一章分为三个部分:1. Runtime动态添加实现方法 2. Runtime动态方法传递 3. Runtime更换方法; 核心都是 “消息动态解析”。这一篇也叫淘气篇
第一部分:“动态添加实现方法”: 只写实例方法,但是不提供方法的实现。验证当找不到方法的实现时,动态添加方法
创建Boby类
// Boby.h
@interface Boby : NSObject
@property (nonatomic, copy) NSString *name;
- (void)eating;
@end
- ---- > // Boby.m
#import "Boby.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation Boby
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
// 动态添加方法
if ([NSStringFromSelector(sel) isEqualToString:@"eating"]) {
class_addMethod(self, sel, (IMP)eateat, "v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void eateat(id self, SEL cmd)
{
NSLog(@"%@ 吃货就知道吃。。。。!。 倒架子。。。",((Boby *)self).name);
}
*----
int main(int argc, const char * argv[]) {
Boby *minBoby = [[Boby alloc] init];
minBoby.name = @"哈利波特";
[minBoby eating];
return 0;
}
看我们打印的结果吧:
`
2016-04-11 21:16:56.967 RuntimeLine[2348:195944] 哈利波特 吃货就知道吃。。。。!。 倒架子。。。
`
> 呵呵, 。好像冤枉了,.h只是提供了个API,.m并没有实现这个API,意思就是人家想干,却是没干,结果就是干了, 反正成果摆在哪儿,这是偷干,额,不对,暗度陈仓 类里面调用函数,真是骗到了,哎
人家.m没干,没有`eating`的方法实现,因此在调用此方法的时候,会调用`resolveInstanceMethod`方法,却是动态添加了方法。他还可以返回No,继续向下传递。接下来。。。
###第二部分:“动态方法传递”: 本来朕没干,可以偷干的,却给波特干了
下面刚才的代码,添加一个Cat
* > 创建Cat类
// Cat.h
import <Foundation/Foundation.h>
@interface Cat : NSObject
@property (copy, nonatomic) NSString * name;
@end.h
@interface Boby : NSObject
@property (nonatomic, copy) NSString *name;
- (void)eating;
@end
* ---- > // Cat.h.m
import "Cat.h"
import "Boby.h"
@implementation Cat
// 1. 我是拒绝的 不动态添加方法, 返回NO 进入2
- (BOOL)resolveInstanceMethod:(SEL)sel
{
return NO;
}
// 2. 我再次拒绝,不指定备选对象响应aselector,进入3
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
// 3. 我还是拒绝,给他返回方法选择器, 进入4 - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"eating"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 4. 算了,答应了,但是不能这么爽快,不然,修改调用对象 - (void)forwardInvocation:(NSInvocation *)anInvocation
{
Boby *boby = [[Boby alloc] init];
boby.name = @"波特哥哥";
[anInvocation invokeWithTarget:boby];
}
*----
*
> ````
//记住应该这样的
int main(int argc, const char * argv[]) {
Cat * cat = [[Cat alloc] init];
cat.name = @"我是朕,铲屎官。。。"; //记住应该这样的
((void(*)(id, SEL))objc_msgSend)((id)cat, @selector(eating));
return 0;
}
//记住应该这样的
结果打印是这样的:
2016-04-11 21:56:07.323 RuntimeLine[2483:214357] 波特哥哥 吃货就知道吃。。。。!。 倒架子。。。
喵喵, 。坑爹呀,. 铲屎官,为毛没朕的份呢???,意思就是本来朕偷干的,却是给波特兄弟偷干了,.
其实铲屎官使坏了,Cat(朕)在.m娇气,冷艳,三次拒绝之后又自己说给波特的,结果呢?铲屎的成功更换了对象,把对象更换为波特兄弟了,朕只会跳舞不行哦, 还要会魔法才行,看来讨好铲屎官才是王道!!!
第三部分:“更换方法” 干了一件别的事
还是用Boby类来搞
// Boby.h
@interface Boby : NSObject
@end
- ---- > // Boby.m
#import "Boby.h"
#if TARGET_IPHONE_SIMULATOR
#import <objc/runtime.h>
#else
#import <objc/runtime.h>
#import <objc/message.h>
#endif
@implementation Boby
// 1. 我是拒绝的 不动态添加方法, 返回NO 进入2
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
return NO;
}
// 2. 我再次拒绝,不指定备选对象响应aselector,进入3
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return nil;
}
// 3. 我还是拒绝,给他返回方法选择器, 进入4
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
if ([NSStringFromSelector(aSelector) isEqualToString:@"eating"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
// 4. 算了,答应了,但是不能这么爽快,不然,修改调用对象
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setSelector:@selector(changeMagic)];
[anInvocation invokeWithTarget:self];
}
// 如果forwardInvocation没实现,就会调用这个方法
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
NSLog(@"消息处长不干了: %@", NSStringFromSelector(aSelector));
}
- (void)changeMagic
{
NSLog(@"变魔法。。。。");
}
*----
int main(int argc, const char * argv[]) {
Boby minBoby = [[Boby alloc] init];
((void()(id, SEL))objc_msgSend)((id)minBoby, @selector(eating));
return 0;
}
打印的结果:
`
2016-04-11 23:37:50.890 RuntimeLine[3180:255959] 变魔法。。。。
`
更换了方法,有吃变为了魔法了, 干了一件别的事。。。什么感觉
> ###案例小结:
看看我们都干了什么?!What have we done?!!!
此实战内容是
`resolveInstanceMethod:(SEL)sel
forwardingTargetForSelector:(SEL)aSelector
methodSignatureForSelector:(SEL)aSelector
forwardInvocation:(NSInvocation *)anInvocation
doesNotRecognizeSelector:(SEL)aSelector`
这个方法使用而已,没什么难的吧,就当学习一下scrollview这么简单吧
回顾一下:1. 动态添加实现方法 2. 动态方法传递 3. 更换方法。
下一篇继续: [《Runtime梳理(四)》](http://www.jianshu.com/p/65dd981334d6)