首页 > 技术文章 > Runtime 一个必须会的技能的使用方法

marlonxlj 2017-01-20 14:43 原文


Runtime 一个必须会的技能


前言:

Runtime是iOS的一个运行时机制,在iOS的开发中很多时候会使用到。在大部分时候很多的人总是一知半解的,有一种朦胧之感,似识非识的感觉。所在就找了很多的资料,也看别人的视频。在此做一个笔记,以便忘记了好查找。

Demo下载地址

objc_class类
struct objc_class

{

    // isa指针指向Meta Class,因为Objc的类的本身也是一个Object,

    // 为了处理这个关系,runtime就创造了Meta Class,

    // 当给类发送[NSObject alloc]这样消息时,实际上是把这个消息发给了Class Object

    Class isa OBJC_ISA_AVAILABILITY;

#if !__OBJC2__

    Class super_class OBJC2_UNAVAILABLE; // 父类

    const char *name OBJC2_UNAVAILABLE; // 类名

    long version OBJC2_UNAVAILABLE; // 类的版本信息,默认为0

    long info OBJC2_UNAVAILABLE; // 类信息,供运行期使用的一些位标识

    long instance_size OBJC2_UNAVAILABLE; // 该类的实例变量大小

    struct objc_ivar_list *ivars OBJC2_UNAVAILABLE; // 该类的成员变量链表

    struct objc_method_list **methodLists OBJC2_UNAVAILABLE; // 方法定义的链表

    // 方法缓存,对象接到一个消息会根据isa指针查找消息对象,

    // 这时会在method Lists中遍历,

    // 如果cache了,常用的方法调用时就能够提高调用的效率。

    // 这个方法缓存只存在一份,不是每个类的实例对象都有一个方法缓存

    // 子类会在自己的方法缓存中缓存父类的方法,父类在自己的方法缓存中也会缓存自己的方法,而不是说子类就不缓存父类方法了

    struct objc_cache *cache OBJC2_UNAVAILABLE;

    struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // 协议链表

#endif

} OBJC2_UNAVAILABLE;

 objc在向一个对象发送消息时,runtime库会根据对象的isa指针找到该对象实际所属的类,然后在该类中的方法列表以及其父类方法列表中寻找方法运行,然后再发送消息的时候,objc_msgSend方法不会返回值,所谓的返回内容都是具体调用时执行的。

如果向一个nil对象发送消息,首先在寻找对象的isa指针时就是0地址返回了,所以不会出现任何错误
objc_ivar_list和objc_method_list
//成员列表
struct objc_ivar_list {
    int ivar_count
OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space
OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_ivar ivar_list[1]
OBJC2_UNAVAILABLE;
}
OBJC2_UNAVAILABLE;

//方法列表
struct objc_method_list {
    struct objc_method_list *obsolete
OBJC2_UNAVAILABLE;
    int method_count
OBJC2_UNAVAILABLE;
#ifdef __LP64__
    int space
OBJC2_UNAVAILABLE;
#endif
    /* variable length structure */
    struct objc_method method_list[1]
OBJC2_UNAVAILABLE;
}
 

1.关联性

//关联对象,绑定
        /**
         * 1.目标
         * 2.标识id
         * 3.value就是值,在此处是号码
         * 4.策略
         */
        objc_setAssociatedObject(alert, CallBtnKey,[NSString stringWithFormat:@"%@",_dataSource[indexPath.row]], OBJC_ASSOCIATION_RETAIN_NONATOMIC);
        

在需要接收关联的结果的地方进行处理:

//取出关联的值
        NSString *str = objc_getAssociatedObject(alertView, CallBtnKey);
        NSString *callStr = [NSString stringWithFormat:@"tel://%@",str];
        
        [[UIApplication sharedApplication] openURL:[NSURL URLWithString:callStr]];
        
给Category绑定方法
#import <UIKit/UIKit.h>
#import <objc/runtime.h>
typedef void (^TapBlock)(UIButton *sender);

@interface UIButton (XLJBlock)

- (void)tapWithEvent:(UIControlEvents )controlEvent withBlock:(TapBlock)tapBlock;

@end

#import "UIButton+XLJBlock.h"

static const void *ButtonKey = &ButtonKey;

@implementation UIButton (XLJBlock)

- (void)tapWithEvent:(UIControlEvents)controlEvent withBlock:(TapBlock)tapBlock
{
    objc_setAssociatedObject(self, ButtonKey, tapBlock, OBJC_ASSOCIATION_COPY_NONATOMIC);
    
    [self addTarget:self action:@selector(buttonClick:) forControlEvents:controlEvent];
}

- (void)buttonClick:(UIButton *)sender
{
   TapBlock tapBlock = objc_getAssociatedObject(sender, ButtonKey);
    
    if (tapBlock) {
        tapBlock(sender);
    }
}

@end


  • 可以利用method_exchangeImplementations来交换2个方法的IMP
  • 可以利用class_replaceMethod方法来替换原有的方法
  • 可以利用method_setImplementation来直接设置某个方法的IMP
消息机制
//需要包含这个头文件
#import <objc/message.h>

//消息事件,修改按钮的背景颜色为橙色;原来为红色
    objc_msgSend(btn, @selector(setBackgroundColor:), [UIColor orangeColor]);

objc_msgForward消息转发

_objc_msgForward是IMP类型,用于消息转发:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。
IMP msgForward = _objc_msgForward;如果手动调用objcmsgForward,将跳过查找IMP的过程,而是直触发"消息转发",时入如下流程:

1.+ (BOOL)resolveInstanceMethod:(SEL)sel实现方法,指定是否动态添加方法。若返回NO,则进入下一步,若返回YES,则通过calss_addMethod函数动态添加方法,消息得到处理,流程完毕
2.在第一步返回NO时,就会进入(id)forwardingTargetForSelector:(SEL)aSelector方法,这是运时给我们的第二次机会,用于指定哪个对象响应selector.不能指定self,若返回nil,表示没有响应者,则会进入第三步。若返回某个对象,则会调用该对象的方法。
3.若第二步返回的是nil,则我们首先要通过- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector来指定方法签名,若返回nil,则表示不做处理,若返回方法签名,则会进入下一步
4.当第三返回方法的方法签名后,就会调用- (void)forwardInvocation:(NSInvocation *)anInvocation方法,我们可以通过anInvocation对象做很多的处理,比如修改实现方法,修改响应对象等。
5.若没有实现- (void)forwardInvocation:(NSInvocation *)anInvocation方法,就会进入-(void)doesNotRecognizeSelector:(SEL)aSelector方法,若我们没有实现这个方法,那么就会crash掉,然后提示打不到响应的方法,到此,动态解析的流程就结束了。

2.交换方法:exchangeIMP

推荐阅读