美文网首页
Runtime 消息的实例

Runtime 消息的实例

作者: Aliyunyun | 来源:发表于2016-03-27 20:58 被阅读22次

    本文主要记录了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]);
    
    消息转发.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 <Foundation/Foundation.h>
    
    @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 <Foundation/Foundation.h>
    
    @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 <Foundation/Foundation.h>
    
    @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 <UIKit/UIKit.h>
    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 消息的实例

          本文链接:https://www.haomeiwen.com/subject/tghklttx.html