runtime简单的使用

作者: PandaXiong | 来源:发表于2016-06-24 16:19 被阅读152次

最近在研究runtime,思考良久觉得好像在项目中并不实用。在研究了一些第三方库,才知道runtime真是黑魔法。
OC的函数调用为消息发送机制,属于动态调用过程。在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。

使用场景

利用runtime黑魔法我们可以实现很多意想不到的功能。
下面梳理下runtime的常用的使用场景:

  1. 动态的添加对象的成员变量和方法
  2. 动态交换两个方法的实现
  3. 实现分类也可以添加属性
  4. 实现NSCoding的自动归档和解档
  5. 实现字典转模型的自动转换

正式开练

1.Category添加属性

直接在Category中添加属性,是无效的。我们这里需要使用runtime来实现
eg:在写一个图片缓存控件时,需要给UIImageView添加imageURL属性。我们通过setter与getter方法来设置和获取属性,添加属性我们需要完成它的setter与getter方法。

#import "objc/runtime.h"
#import "UIImageView+WebCache.h"

static const void *imageURLKey = &imageURLKey;

@implementation UIImageView (WebCache)
- (NSString *)imageURL {
    return objc_getAssociatedObject(self, imageURLKey);
}
- (void)setImageURL:(NSString *)imageURL {
    objc_setAssociatedObject(self, imageURLKey, imageURL, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
@end
2.更改系统方法的实现

接手一个新工程,找到页面对应的UIViewController是很困难的(特别是在命名不规范情况下)。想实现进入那个页面就打印哪个页面进来了
创建一个 UIViewController 的分类,在+(void)load中实现方法的交换。(+(void)load只要类所在文件被引用就会被调用。所以如果类没有被引用进项目,就不会有load调用,方法只会被调用一次。)

#import "UIViewController+Extension.h"
#import <objc/runtime.h>

@implementation UIViewController (Extension)

+(void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        
        //获取UIViewController的方法,objc_method结构体指针
        Method system = class_getInstanceMethod([self class], @selector(viewWillAppear:));
        Method change = class_getInstanceMethod([self class], @selector(viewWillAppearNew));
        //获取新创建方法
        Method systemDis = class_getInstanceMethod([self class], @selector(viewWillDisappear:));
        Method changeDis = class_getInstanceMethod([self class], @selector(viewWillDisappearNew));
        //交换两个方法的实现
        method_exchangeImplementations(system, change);
        method_exchangeImplementations(systemDis, changeDis);
        
    });
}
- (void)viewWillAppearNew {
    [self viewWillAppearNew];
#if TARGET_IPHONE_SIMULATOR
     NSLog(@"AAAA%@页面出现了", [self class]);
#endif
   
}
- (void)viewWillDisappearNew {
    [self viewWillDisappearNew];
#if TARGET_IPHONE_SIMULATOR
    NSLog(@"AAAA%@页面消失了", [self class]);
#endif
    
}
@end
3. objc_msgSend的作用

OC中像对象发送消息id returnValue = [someObject messageName:parameter];
在运行时会将其转换成C语言函数

id returnValue = objc_msgSend(someObject,  
                              @selector(messageName:),  
                              parameter); 

void objc_msgSend(id self, SEL cmd, ...)是消息转发机制的核心函数。

在Objective-C中,如果向某对象传递消息,那就会使用动态绑定机制来决定需要调用的方法。在底层,所有方法都是普通的C语言函数,然而对象收到消息之后,究竟该调用哪个方法则完全于运行期决定,甚至可以在程序运行时改变,这些特性使得Objective-C成为一门真正的动态语言。

由于objc_msgSend函数本身是无返回值无参数的函数, 所以要给它强制转换类型代码如下:

Person *person = [[Person alloc]init];
 ((void (*) (id, SEL)) (void *)objc_msgSend)(person, @selector(say));
4. runtime实现自定义对象归档

先熟悉两个方法

/**
 *  Ivar 属性成员
 *  @param Class 当前对象的类
 *  @param outCount 无符号整型的变量指针,存储属性的个数
 * @return IvarList属性数组
 */
OBJC_EXPORTIvar *class_copyIvarList(Class cls, unsigned int *outCount) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

/** 
 * 返回一个实例变量的名字.
 * 
 * @param v 实例变量.
 * 
 * @return A c类型的字符串 变量名.
 */
OBJC_EXPORTconst char *ivar_getName(Ivar v) 
     __OSX_AVAILABLE_STARTING(__MAC_10_5, __IPHONE_2_0);

#import "NSObject+Archiver.h"
#import <objc/runtime.h>
@implementation NSObject (Archiver)
//解档
- (id)initWithCoder:(NSCoder *)decoder
{
    if (self = [self init]) {
        unsigned int nCount = 0;
        //获取类中所有成员变量名
        Ivar *ivar = class_copyIvarList([self class], &nCount);
        for (int i = 0; i<nCount; i++) {
            Ivar iva = ivar[i];
            const char *name = ivar_getName(iva);
            //成员变量名
            NSString *strName = [NSString stringWithUTF8String:name];
            //进行解档取值
            id value = [decoder decodeObjectForKey:strName];
            //利用KVC对属性赋值(判断不为空)
            if (![value isKindOfClass:[NSNull class]] && value != nil) {
                [self setValue:value forKey:strName];
            }
        }
        free(ivar);
    }
    return self;
}

//归档
- (void)encodeWithCoder:(NSCoder *)encoder
{
    unsigned int nCount;
    Ivar *ivar = class_copyIvarList([self class], &nCount);
    for (int i=0; i<nCount; i++) {
        Ivar iva = ivar[i];
        const char *name = ivar_getName(iva);
        //成员变量名
        NSString *strName = [NSString stringWithUTF8String:name];
        //利用KVC取值
        id value = [self valueForKey:strName];
        //把取出的属性值进行归档
        [encoder encodeObject:value forKey:strName];
    }
    free(ivar);
}
@end

使用这个分类 所有的类都可以直接实现NSCodeing协议。

相关文章

  • Runtime全面剖析之原理篇

    如果想了解Runtime的实际应用请看Runtime全面剖析之简单使用 一:Runtime简介二: Runtime...

  • runtime 简单使用

    import UIKit class User: NSObject,NSCopying,NSCoding { } ...

  • Runtime简单使用

    ios runtime 然后我们打开终端,在命令行找到cd到文件目录,然后中输入: clang -rewrite-...

  • runtime简单使用

    将字典转化为模型,面向模型开发,是在开发中最为常用的功能。利用KVC可以将字典转换为模型,但是前提有三个约束,一个...

  • Runtime的简单使用

    Runtime简介 Runtime是一套底层的C语言API(包含了很多强大实用的C语言数据类型和C语言函数), 实...

  • runtime简单的使用

    最近在研究runtime,思考良久觉得好像在项目中并不实用。在研究了一些第三方库,才知道runtime真是黑魔法。...

  • Runtime的简单使用

    1.方法的交换使用那么在什么情况下需要使用到这个呢,让我来举个栗子?,请看以下代码,如果当照片的名字是错误的话或者...

  • RunTime的简单使用

    一、runtime简介 1.RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的...

  • runtime的简单使用

    runtime简介 RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机...

  • iOS Runtime简单使用

    原文在此 Runtime 用Objective-C写的代码,在运行过程中都会被转化成C代码去执行。比如说OC的方法...

网友评论

本文标题:runtime简单的使用

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