Runtime梳理(三)

挖就挖底层.png

继上《Runtime梳理(二)》

我们这一章分为三个部分: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)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,838评论 0 9
  • 继上Runtime梳理(四) 通过前面的学习,我们了解到Objective-C的动态特性:Objective-C不...
    小名一峰阅读 784评论 0 3
  • 一、概述 Objective-C语言是一门动态语言,它将很多静态语言在编译和链接期所做的事推迟到运行时处理。这种动...
    Fly晴天里Fly阅读 1,224评论 0 6
  • 我们常常会听说 Objective-C 是一门动态语言,那么这个「动态」表现在哪呢?我想最主要的表现就是 Obje...
    Ethan_Struggle阅读 2,237评论 0 7
  • 对于从事 iOS 开发人员来说,所有的人都会答出【runtime 是运行时】什么情况下用runtime?大部分人能...
    梦夜繁星阅读 3,744评论 7 64