Runtime 消息的实例

本文主要记录了Runtime的消息转发过程和我们可以利用这个消息转发机制来做一下事情。

消息


object-c 的消息[receive selector],最终都会变成 objc_msgSend(receive,selector),objc_msgSend只负责给消息接受者发送消息,寻找我们需要调用的方法。

UIButton *btn = [UIButton new];
 [button setBackgroundColor:[UIColor redColor]];
相当于
objc_msgSend(button,@selector(setBackgroundColor:), [UIColor redColor]);
Runtime 消息的实例_第1张图片
消息转发.png
IMP

typedef id (*IMP)(id, SEL, ...); 本质上就是一个C语言的函数指针,他只真正能找到我们要执行的方法的地址。

Runtime 方法调用过程

1、判断target是否为空,selector的方法是否需要执行,是否可以执行
2、在cache中方法,如果没有找到
3、就会在方法列表中寻找,一直到NSObject的方法
4、如果找不到,就要进行动态方法解析
5、消息重定向

这里主要是介绍方法动态解析和消息转发,就对objc_class和objc_cache IMP 不介绍了。

Runtime 动态方法解析

+ (BOOL)resolveClassMethod:(SEL)aSel
+ (BOOL)resolveInstanceMethod:(SEL)aSel

#import 

@interface Foot : NSObject

- (void)resolveInstanMethod;

@end


#import "Foot.h"
#import "Objc/runtime.h"
@implementation Foot

void  footCanBeEat(id self, SEL _cmd)
{
    NSLog(@"foot can be eat");
}

+ (BOOL)resolveInstanceMethod:(SEL)aSel
{
    if (aSel == @selector(resolveInstanMethod)) {
        class_addMethod([self class], @selector(resolveInstanMethod), (IMP)footCanBeEat, "v@:");
    }
    return NO;
}


@end


调用过程
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Foot *YY = [[Foot alloc]init];
    [YY resolveInstanMethod];
    
}

Foot 想外声明了一个方法,resolveInstanMethod, 但是里面并没有这个方法的实现。而是通过重写 resolveInstanceMethod, 通过class_addMethod,来实现动态添加方法。

如果没有重写方法的动态解析,Runtime就会做方法的重定向,重定向就是将消息重新发送给另外一个对象。

重定向

如上面的例子,如果我们没有实现方法的动态解析,我们可以讲发送给我的消息,转发给另外一个对象。

*例子

#import 

@interface Foot : NSObject
@end


void  footCanBeEat(id self, SEL _cmd)
{
    NSLog(@"foot can be eat");
}

+ (BOOL)resolveInstanceMethod:(SEL)aSel
{
    if (aSel == @selector(resolveInstanMethod)) {
        class_addMethod([self class], @selector(resolveInstanMethod), (IMP)footCanBeEat, "v@:");
    }
    return NO;
}

@end




#import 

@interface Person : NSObject

- (void)resolveInstanMethod;

@end


*********************************************
#import "Person.h"
#import "Foot.h"
#import "Objc/runtime.h"

@interface Person ()

@property (nonatomic, strong) Foot *myFoot;

@end

@implementation Person

- (instancetype)init
{
    self = [super init];
    if (self) {
        _myFoot = [[Foot alloc]init];
    }
    return self;
}

-(id)forwardingTargetForSelector:(SEL)aSelector
{
    if (aSelector == @selector(resolveInstanMethod)) {
        return _myFoot;
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end

*****************************************************
- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    Person *YY = [[Person alloc]init];
    [YY resolveInstanMethod];
    
}

从这里看见,person的事例YY 调用了resolveInstanMethod,但是他根本就没有实现这个方法。也没有做方法的动态解析,而是走了方法的重定向,将这个消息转发给了Foot的实体myfoot,Foot也没有声明resolveInstanMethod,但是他动态解析了这个方法。

所以上面方法的最终实现这是Foot,消息最开始的target是Person这样就实现了方法的重定向。

关联属性


关联属性,就是用一个关键字,给一个对象附加一个属性。
方法:

 objc_setAssociatedObject()
 objc_getAssociatedObject()

例子1:
通过buttonPhoneKey,我们将phoneNUmber和button绑定到了一起,就相当于给这个button增加了一个属性。属性关联一般的使用场景就是在category给一个类增加一个属性。

- (void)viewDidload{
    static const void *buttonPhoneKey = "phoneNum";
    NSString *phoneNumber = @"18126504453";
    
    UIButton *button = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)];
    [button setTitle:@"associate" forState:UIControlStateNormal];
    [button setTitleColor:[UIColor greenColor] forState:UIControlStateNormal];
    [button addTarget:self action:@selector(buttonAction) forControlEvents:UIControlEventTouchUpInside];
    
    objc_setAssociatedObject(button, buttonPhoneKey, phoneNumber, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

    [self.view addSubview:button];
}

- (void)buttonAction:(UIButton *)btn
{
   NSString *phoneNum = objc_getAssociatedObject(btn, buttonPhoneKey);
    NSLog(@"phone num = %@ ", phoneNum);
}

例子2:通过属性关联,给UIButton 的even添加block,这样Button的事件就可以很方便的使用了,特别是很多需要用tag来区分的事件。

#import 
typedef    void(^tapAction)(UIButton *sender) ;
@interface UIButton (Block)
- (void)controlEvent:(UIControlEvents )event withBlock:(tapAction)block;
@end


#import "UIButton+Block.h"
#import "objc/runtime.h"

static const void *buttonAssociateKey = &buttonAssociateKey;
@implementation UIButton (Block)

- (void)controlEvent:(UIControlEvents )event withBlock:(tapAction)block
{
    objc_setAssociatedObject(self, buttonAssociateKey, block, OBJC_ASSOCIATION_COPY_NONATOMIC);
    [self addTarget:self action:@selector(buttonAction:) forControlEvents:event];
}

- (void)buttonAction:(UIButton *)btn
{
    tapAction block =  objc_getAssociatedObject(btn, buttonAssociateKey);
    if (block) {
        block(btn);
    }
}
@end

你可能感兴趣的:(Runtime 消息的实例)