iOS Runtime 初体验

本文介绍如何使用iOS Runtime技术访问和修改类的私有方法及变量。通过实例演示了如何获取类的所有变量、属性和方法,以及如何给变量赋值并运行私有方法。

初学iOS,虽然只是有了简单的认识,但是也确实令我激动了一下。之前想要访问一个类的私有方法,可以通过分类和KVC实现,但是比较有局限性。现在运用所学的一点runtime知识,再次访问私有方法和变量,并且能过通过runtime对它们进行赋值,甚至更改方法。
首先,需要具备的一点基本知识有:
What is runtime?
runtime是一套比较底层的纯C语言API, 属于1个C语言库,OC程序运行过程时, 最终转成了runtime的C语言代码
举例:
OC :
[Apple alloc]
[[Apple alloc] init]
runtime :
objc_msgSend(“Apple” , “alloc”)
objc_msgSend(objc_msgSend(“Apple” , “alloc”), “init”)

用到的一些runtime知识:
1> Ivar : 成员变量
2> Method : 成员方法
3> class_copyIvarList:获取类的所有属性变量,count记录变量的数量IVar是runtime声明的一个宏,是实例变量的意思,instance variable,在runtime中定义为 typedef struct objc_ivar *Ivari
4> var_getName:将IVar变量转化为字符串
5> ivar_getTypeEncoding:获取IVar的类型
6> Method:runtime声明的一个宏,表示一个方法,
typedef struct objc_method *Method;
7> class_copyMethodList:获取所有方法
8> method_getName:读取一个Method类型的变量

下面开始具体操作:
1.首先,创建一个Classes类
Classes.h中定义不同作用域的成员变量和属性,以及一个可见的方法:

#import <Foundation/Foundation.h>
@interface Classes : NSObject{
    @private
        NSNumber *classNo;
    @public
    NSString *classDesc;
}
@property (nonatomic,copy) NSString *className;
- (void)printInfo;
@end

Classes.m中定义并实现一个私有方法-printInfo1:

#import "Classes.h"
@interface Classes()
- (void)setInfo:(NSNumber *)classNo
       WithDesc:(NSString *)classDesc
       WithName:(NSString *)className;
@end

@implementation Classes
- (void)setInfo:(NSNumber *)classNO WithDesc:(NSString *)classDESC  WithName:(NSString *)className{
    _className = className;
    self ->classNo = classNO;
    classDesc = classDESC ;
}

- (void)printInfo{
    NSLog(@"%d %@ %@",[self ->classNo intValue],_className,classDesc);
}

- (void)printInfo1{
    NSLog(@"%d %@ %@",[self ->classNo intValue],_className,classDesc);
}
@end

2.ViewController中导入runtime.h:
#import <objc/runtime.h>
3.先来访问一下该类的所有变量和属性吧,新建方法parseClass:

- (void)parseClass:(NSString *)className{
    //根据字符串得到类
    Class temClass = NSClassFromString(className);//传入你想查看类的类名
    unsigned int outCount = 0;
    //得到所有类的变量和属性
    Ivar *varList =  class_copyIvarList(temClass, &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar var = varList[i];
        const char *type = ivar_getTypeEncoding(var);
        const char *name = ivar_getName(var);
        NSLog(@"type:%s----name:%s",type,name);
    }
}

接下来调用[self parseClass: @"Classes"]; 运行并查看
这里写图片描述
可以看到,各种类型的变量属性都能访问得到。

4.再接下来,给变量属性赋值,并打印出来

- (void)setValues:(NSString *)className{
    //根据字符串得到类
    Class temClass = NSClassFromString(className);//传入你想查看类的类名
    //实例化一个对象
    id c = [[temClass alloc] init ];
    unsigned int outCount = 0;
    //得到所有类的变量和属性
    Ivar *varList =  class_copyIvarList(temClass, &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar var = varList[i];
        const char *type = ivar_getTypeEncoding(var);
        const char *name = ivar_getName(var);
        //Classes中有变量classNo,classDesc,_className
        NSString *varName = [NSString stringWithUTF8String:name];
            //赋值三要素:对象,变量,具体值
        if ([varName isEqualToString:@"classNo"]) {
            object_setIvar(c, var, [[NSNumber alloc] initWithInt:3]);
        }else if([varName isEqualToString:@"classDesc"]){
            object_setIvar(c, var, @"这是描述信息");
        }else if([varName isEqualToString:@"_className"]){
            object_setIvar(c, var, @"这时变量名");
        }
        NSLog(@"type:%s----name:%s",type,name);
    }
    //获取方法,运行,访问Classes中私有方法
    SEL method =  NSSelectorFromString(@"printInfo1");
    //[c performSelector:method withObject:nil];//可以用OC 的方法
    objc_msgSend(c,method);

调用并运行
这里写图片描述
可以看到已经被赋值了

5.然后再写一个方法查看类中所有的方法:

- (void)parseMethod:(NSString *)className{
    //根据字符串得到类
    Class temClass = NSClassFromString(className);//传入你想查看类的类名
    unsigned int outCount = 0;
    //得到所有类的变量和属性
    Method *methodList = class_copyMethodList(temClass, &outCount);
    for (int i = 0; i < outCount; i++) {
        SEL var = method_getName(methodList[i]);
        const char *name = sel_getName(var);
        NSLog(@"name:%s",name);//
    }
}

调用并运行:
这里写图片描述
可以看到所有函数都在里面了,至于.cxx_destruct能看出来是个析构函数,但是我并没有定义它,哪里来的呢?查了一下,它是编译器自动生成的,用来ARC下自动释放对象的成员变量。

6.最后再尝试一下运行时对方法进行小的改动:
通过 method_exchangeImplementations()来交换方法的实现,我将原来定义的大小写方法交换一下:

- (void)changeMethod{
    NSLog(@"Lower case of ABCde:%@",[@"ABCde" lowercaseString]);
    NSLog(@"Upper case of ABCde:%@",[@"ABCde" uppercaseString]);
    NSLog(@"*****************************************");
    Method lowerMethod = class_getInstanceMethod([NSString class], @selector(lowercaseString));
    Method upperMethod = class_getInstanceMethod([NSString class], @selector(uppercaseString));
    method_exchangeImplementations(lowerMethod, upperMethod);
    NSLog(@"Lower case of ABCde:%@",[@"ABCde" lowercaseString]);
    NSLog(@"Upper case of ABCde:%@",[@"ABCde" uppercaseString]);
}

运行之:
这里写图片描述
很直观的看出,方法被改了。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值