初学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]);
}
运行之:
很直观的看出,方法被改了。
本文介绍如何使用iOS Runtime技术访问和修改类的私有方法及变量。通过实例演示了如何获取类的所有变量、属性和方法,以及如何给变量赋值并运行私有方法。
1417

被折叠的 条评论
为什么被折叠?



