runtime的一些理解与用法

来源:互联网 发布:华藏净宗网络直播台 编辑:程序博客网 时间:2024/05/22 05:04

Runtime概述:

    runtime是一套由C语言API组合成的Runtime库;并尽可能把代码的执行决策推迟到运行时。OC是一门动态语言,OC代码最终都会转换成底层Runtime的代码。

- Runtime的使用场景:


1.动态获取、创建类;
//    动态创建类    Class pCreateClass = objc_allocateClassPair([NSObject class], "pClass", 0);
//    动态获取类    NSLog(@"pCreateClass's meta class is %p",object_getClass([pCreateClass class]));

2.动态为一个类增加属性或方法;首先要明确 Objective-C 作为一种动态语言,它会将部分代码放置在运行时的过程中执行,而不是编译时,所以在执行代码时,不仅仅需要的是编译器,也同时需要一个运行时环境(Runtime),为了满足一些需求,苹果开源了 Runtime Source 并提供了开放的 api 供开发者使用。其次,class_addMethod运用场景。当项目中,需要继承某一个类(subclass),但是父类中并没有提供我需要的调用方法,而又不清楚父类中某些方法的具体实现;或者,需要为这个类写一个分类(category),在这个分类中,可能需要替换/新增某个方法(注意:不推荐在分类中重写方法,而且也无法通过 super 来获取所谓父类的方法)。大致在这两种情况下,我们可以通过 class_addMethod 来实现我们想要的效果。在 Objective-C 中,正常的调用方法是通过消息机制(message)来实现的,那么如果类中没有找到发送的消息方法,系统就会进入找不到该方法的处理流程中,如果在这个流程中,我们加入我们所需要的新方法,就能实现运行过程中的动态添加了。这个流程或者说机制,就是 Objective-C 的 Message Forwarding     2.1增加属性
objc_registerClassPair(pCreateClass);    id myobj = [[pCreateClass alloc] init];    NSLog(@"---->%@",[myobj class]);    //生成了一个实例化对象//    id myobj = [[pCreateClass alloc] init];    NSString *str = @"testString";    //给刚刚添加的变量赋值    //    object_setInstanceVariable(myobj, "m_strTest", (void *)&str);在ARC下不允许使用    [myobj setValue:str forKey:@"m_strTest"];
    2.2增加方法
//    Class cls:cls 参数表示需要添加新方法的类。//    SEL name:name 参数表示 selector 的方法名称,可以根据喜好自己进行命名。//    IMP imp:imp 即 implementation ,表示由编译器生成的、指向实现方法的指针。也就是说,这个指针指向的方法就是我们要添加的方法。//    const char *types:最后一个参数 *types 表示我们要添加的方法的返回值和参数。    class_addMethod(pCreateClass, @selector(pClassMethod:), (IMP)pClassMethod1, "v@:");    [myobj pClassMethod:10];
//C 语言风格写新函数//self 代表着函数本身;而 _cmd 则是一个 SEL 数据体;包含了具体的方法地址,在之后可以随意添加其他参数static void pClassMethod1(id self, SEL _cmd, int sender){    Ivar v = class_getInstanceVariable([self class], "m_strTest");    //返回名为m_strTest的ivar的变量的值    id strTest = object_getIvar(self, v);    NSLog(@"%@", strTest);    NSLog(@"int sender is %d", sender);}//OC 语言风格写新函数- (void)pClassMethod1:(int)sender {    Ivar v = class_getInstanceVariable([self class], "m_strTest");    //返回名为m_strTest的ivar的变量的值    id strTest = object_getIvar(self, v);    NSLog(@"%@", strTest);    NSLog(@"int sender is %d", sender);}- (void)pClassMethod:(int)sender{    NSLog(@"pClassMethod");}

这里写图片描述


3.在程序运行时遍历类中的实例变量、属性或方法
RunTimeData* pData = [[RunTimeData alloc] init];    NSLog(@"%@",[pData class]);    //遍历实例变量    Class objectClass = pData.class;    unsigned int nOutCount = 0;    Ivar* ivarPtr = class_copyIvarList(objectClass, &nOutCount);    for (NSInteger i = 0; i < nOutCount; i++) {        Ivar ivar = ivarPtr[i];        NSLog(@"实例变量:%s",ivar_getName(ivar));    }

这里写图片描述

//遍历属性    objc_property_t* propPtr = class_copyPropertyList(objectClass, &nOutCount);    for (NSInteger i = 0; i < nOutCount; i++) {        objc_property_t pPtr = propPtr[i];        NSLog(@"属性:%s",property_getName(pPtr));    }

这里写图片描述

//遍历对象方法(.cxx_destruct这个是类中隐藏的函数,不需要去理会)    Method* methodClass = class_copyMethodList(objectClass, &nOutCount);    for (NSInteger i = 0; i < nOutCount; i++) {        Method method = methodClass[i];        SEL selector = method_getName(method);        NSLog(@"方法:%@",NSStringFromSelector(selector));    }

这里写图片描述


4.调换两个方法的逻辑实现(Method Swizzle)        4.1在iOS中实现AOP(Aspect Oriented Programming(面向切面编程))编程思想的一种方式是Method Swizzling。Method Swizzling利用Runtime特性把一个方法的实现和另一个方法的实现进行替换,在程序运行时修改Dispatch Table里SEL和IMP的映射关系。通过 swizzling method 改变目标函数的 selector 所指向的实现,然后在新的实现中实现附加的操作,完成之后再调用原来的处理逻辑。        4.2Method Swizzling的优势                继承:修改多,无法保证他人一定继承基类                类别:类别中重写方法会覆盖原来的实现,大多时候,重写一个方法是为了添加一些代码,而不是完全取代它。如果两个类别都实现了相同的方法,只有一个类别的方法会被调用到。                AOP优势:减少切面业务的重复编码,减少与其他业务的耦合,把琐碎事务从主业务中分离。

实例:
RunTimeData.m

#import "RunTimeData.h"#import <objc/runtime.h>+ (void)load{//在这里调用时因为+(void)load方法在main()前类载入内存时会调用一次。    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        [RunTimeData swizzeleViewDidLoad];    });}    + (void)swizzeleViewDidLoad{    Class pVC = NSClassFromString(@"ViewController");    Method orgiMethod = class_getInstanceMethod(pVC, @selector(viewDidLoad));    Method newMethod = class_getInstanceMethod([self class], @selector(swizzeleViewDidLoad));    BOOL isAddMethod = class_addMethod(pVC, @selector(swizzeleViewDidLoad), method_getImplementation(newMethod), method_getTypeEncoding(newMethod));    if (isAddMethod) {        Method newMethodInVC = class_getInstanceMethod(pVC, @selector(swizzeleViewDidLoad));        method_exchangeImplementations(orgiMethod, newMethodInVC);    }}//两行代码书写顺序1(注释顺序2)- (void)swizzeleViewDidLoad{    [self swizzeleViewDidLoad];//正常情况下在类中调用,必然进入无限循环。然而,在Method Swizzling中,这样才是正确的用法。理解下其中的逻辑。在load方法中实现了Swizzling,系统SEL的viewDidLoad指向的是swizzeleViewDidLoad的实现,方法SEL名为swizzeleViewDidLoad指向的是系统名为viewDidLoad的实现。所以在调用过程中相当两个方法交叉调用了,并没有导致死循环。    NSLog(@"class --> %@ _cmd==%@",[self class],NSStringFromSelector(_cmd));}//两行代码书写顺序2(注释顺序1)- (void)swizzeleViewDidLoad{    NSLog(@"class --> %@ _cmd==%@",[self class],NSStringFromSelector(_cmd));    [self swizzeleViewDidLoad];}

ViewController.m

- (void)viewDidLoad {    [super viewDidLoad];    NSLog(@"pVC ViewDidLoad");    }

两行代码书写顺序1日志打印结果
这里写图片描述

两行代码书写顺序2日志打印结果
这里写图片描述
这里输出的结果反映出一些问题,具体问题描述可以查看下面的 [Method Swizzle 的一些注意事项]


Method Swizzle
Method Swizzle 的一些注意事项
Objective-C Runtime 1小时入门教程

1 0
原创粉丝点击