概述
RunTime: OC是采用消息机制的,程序在运行的时候转化为c的runtime执行,例如[self viewDidLoad]
被转化为objc_msgSend(self,@selector(viewDidLoad))
runtime有什么用
动态产生一个类,一个成员变量,一个方法
动态修改一个类,一个成员变量,一个方法
动态删除一个类,一个成员变量,一个方法
runtime类库
按照自己需要使用的导入,主要函数在import <objc/message.h>
、import <objc/runtime.h>
。
#import <objc/objc.h>
#import <objc/runtime.h>//包含对类、成员变量、属性、方法的操作
#import <objc/message.h>//包含消息机制
#import <objc/objc-class.h>
#import <objc/objc-api.h>
常用函数
class_copyIvarList()//返回一个指向类的成员变量数组的指针
class_copyPropertyList()//返回一个指向类的属性数组的指针
class_copyMethodList()// 获取类的方法列表
以上方法使用完毕free()释放,避免内存泄漏
runtime定义
- 对对象进行操作的方法一般以object_开头_
- 对类进行操作的方法一般以class_开头_
- 对类或对象的方法进行操作的方法一般以method_开头_
- 对成员变量进行操作的方法一般以ivar_开头_
- 对属性进行操作的方法一般以property_开头开头_
- 对协议进行操作的方法一般以protocol_开头
进入runtime.h查看所有定义,这里列举部分
/// An opaque type that represents a method in a class definition. 一个类型,代表着类定义中的一个方法
typedef struct objc_method *Method;
************ 分割线 ***********************
/// An opaque type that represents an instance variable.代表实例(对象)的变量
typedef struct objc_ivar *Ivar;
************ 分割线 ***********************
/// An opaque type that represents a category.代表一个分类
typedef struct objc_category *Category;
************ 分割线 ***********************
/// An opaque type that represents an Objective-C declared property.代表OC声明的属性
typedef struct objc_property *objc_property_t;
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;//isa 就是指向元类的objc_class结构指针
#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;// 方法链表
struct objc_cache *cache OBJC2_UNAVAILABLE;// 用过的方法缓存链表
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;// 协议链表
#endif
} OBJC2_UNAVAILABLE;
Ivar(成员变量)
Ivar是objc_ivar的指针,包含变量名,变量类型等成员。
定义的类型
typedef objc_ivar * Ivar;
struct objc_ivar {
char *ivar_name;
char *ivar_type;
int ivar_offset;
#ifdef __LP64__
int space;
#endif
}
ivar相关方法
// 获取类中指定名称实例成员变量的信息
Ivar class_getInstanceVariable ( Class cls, const charchar *name );
// 获取类成员变量的信息
Ivar class_getClassVariable ( Class cls, const charchar *name );
// 添加成员变量
BOOL class_addIvar ( Class cls, const charchar *name, size_t size, uint8_t alignment, const charchar *types );
// 获取整个成员变量列表
Ivar * class_copyIvarList ( Class cls, unsigned intint *outCount ); //返回一个指向成员变量信息的数组,数组中每个元素是指向该成员变量信息的objc_ivar结构体的指针。这个数组不包含在父类中声明的变量。outCount指针返回数组的大小。需要注意的是,我们必须使用free()来释放这个数组。
<pre><code>// 获取成员变量名
const charchar * ivar_getName ( Ivar v );
// 获取成员变量类型编码
const charchar * ivar_getTypeEncoding ( Ivar v );
// 获取成员变量的偏移量
ptrdiff_t ivar_getOffset ( Ivar v );
/**
* 设置一个实例对象某个成员属性的值
*
* @param obj 对象
* @param ivar 成员属性
* @param value 成员属性的值
*/
void object_setIvar(id obj, Ivar ivar, id value);
// 获取实例对象某个成员属性的值
id object_getIvar(id obj, Ivar ivar)
添加ivar
运行时规定,只能在objc_allocateClassPair与objc_registerClassPair两个函数之间为类添加变量
例子 类名: hycClass 父类:NSObject
//API:创建一个类和元类
//Params: superclass(父类) name(创建的类名) extraBytes(额外空间通常设置为0)
//OBJC_EXPORT Class objc_allocateClassPair(Class superclass, const char *name,
size_t extraBytes)
Class class_ = objc_allocateClassPair([NSObject Class],"hycClass",0);
//以NSString*为例,添加一个name变量"name_
//变量size sizeof(NSString)
//对齐 指针类型的为log2(sizeof(NSString*))
//类型 @encode(NSString*)
BOOL flag = class_addIvar(class_,"name_",sizeof(NSString*),log2(sizeof(NSString*)),@encode(NSString*));
//为类添加方法
//IMP 是函数指针
// typedef id (*IMP)(id, SEL, ...);
IMP i = imp_implementationWithBlock(^(id this,id some){
NSLog(@"%@",some);
return @111;
});
//注册方法名为 test: 的方法
SEL s = sel_registerName("test:");
class_addMethod(Test, s, i, "i@:");
//结束类的定义
objc_registerClassPair(class_);
ivar示例
Class cls = [Person class];
size_t size = class_getInstanceSize(cls);
NSLog(@"size = %zu", size);
unsigned int ivarCount = 0;
Ivar *ivarList = class_copyIvarList(cls, &ivarCount);
NSLog(@"*************** IvarList ********************");
NSLog(@"ivarCount = %d", ivarCount);
for (int i = 0; i < ivarCount; i++) {
Ivar ivar = ivarList[i];
const charchar *ivar_name = ivar_getName(ivar);
const charchar *typeEncoding = ivar_getTypeEncoding(ivar);
ptrdiff_t offset = ivar_getOffset(ivar);
NSLog(@"ivar_name = %s, typeEncoding = %s, offset = %td", ivar_name, typeEncoding, offset);
}
// 一定要对ivarList做free操作
free(ivarList);
NSLog(@"****************** 设置、获取成员变量的值 ***********************");
Person *p = [[Person alloc] init];
Ivar ivar_name = class_getInstanceVariable(cls, "_name");
object_setIvar(p, ivar_name, @"傻傻木头人");
NSLog(@"-----object_getIvar------name = %@", object_getIvar(p, ivar_name));
objc_property_t(成员属性)
objc_property_attribute_t定义了属性的特性(attribute),它是一个结构体,定义如下
typedef struct {
const charchar *name; // 特性名
const charchar *value; // 特性值
} objc_property_attribute_t;
相关函数
// 获取指定的属性
objc_property_t class_getProperty ( Class cls, const charchar *name );
// 获取属性列表
objc_property_t * class_copyPropertyList ( Class cls, unsigned intint *outCount ); //free()释放,防止内存泄露。
// 为类添加属性
BOOL class_addProperty ( Class cls, const charchar *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
// 替换类的属性
void class_replaceProperty ( Class cls, const charchar *name, const objc_property_attribute_t *attributes, unsigned int attributeCount );
// 获取属性名
const charchar * property_getName ( objc_property_t property );
// 获取属性特性描述字符串
const charchar * property_getAttributes ( objc_property_t property );
// 获取属性中指定的特性
charchar * property_copyAttributeValue ( objc_property_t property, const charchar *attributeName );
// 获取属性的特性列表
objc_property_attribute_t * property_copyAttributeList ( objc_property_t property, unsigned intint *outCount ); //free()释放,防止内存泄露。
示例
Class cls = [Person class];
unsigned int propertyCount = 0; //属性数量
// 类,属性数量(传的地址)
objc_property_t *propertyList = class_copyPropertyList(cls, &propertyCount);
NSLog(@"********************PropertyList******************");
NSLog(@"propertyCount = %d", propertyCount);
for (int i = 0; i < propertyCount; i++) {
objc_property_t property = propertyList[i];
// 属性名字
const charchar *property_name = property_getName(property);
// 描述属性的字符串
const charchar *property_attributes = property_getAttributes(property);
NSLog(@"******* property_name = %s, property_attributes = %s", property_name, property_attributes);
unsigned int attributeCount = 0;
// 获取属性特性列表
objc_property_attribute_t *attributeList = property_copyAttributeList(property, &attributeCount);
NSLog(@"** attributeList ---- attributeCount = %u", attributeCount);
for (int i = 0; i < attributeCount; i++) {
objc_property_attribute_t attribute = attributeList[i];
NSLog(@"name = %s, value = %s" ,attribute.name, attribute.value);
}
free(attributeList); // 一定要对attributeList做free操作
}
free(propertyList);// 一定要对ivarList做free操作
Method
Method类型是一个 objc_method 结构体指针,有三个成员
typedef struct objc_method*Method;
struct objc_method {
SEL method_name; // 方法名称
char *method_typesE; // 参数和返回类型的描述字串
IMP method_imp; // 方法的具体的实现的指针
}
所有方法
// 获取类的方法列表
class_copyMethodList
// 函数调用,但是不接收返回值类型为结构体
method_invoke
// 函数调用,但是接收返回值类型为结构体
method_invoke_stret
// 获取函数名
method_getName
// 获取函数实现IMP
method_getImplementation
// 获取函数type encoding
method_getTypeEncoding
// 复制返回值类型
method_copyReturnType
// 复制参数类型
method_copyArgumentType
// 获取返回值类型
method_getReturnType
// 获取参数个数
method_getNumberOfArguments
// 获取函数参数类型
method_getArgumentType
// 获取函数描述
method_getDescription
// 设置函数实现IMP
method_setImplementation
// 交换函数的实现IMP
method_exchangeImplementations
示例
unsigned int outCount = 0;
// 获取方法列表
Method *methodList = class_copyMethodList(self.class, &outCount);
for (unsigned int i = 0; i < outCount; ++i) {
// 取出一个方法
Method method = methodList[i];
// 取出方法名字转化为SEL
SEL methodName = method_getName(method);
NSLog(@"方法名:%@", NSStringFromSelector(methodName));
// 以下代码段获取方法的参数类型
// 方法的参数数量
unsigned int argumentsCount = method_getNumberOfArguments(method);
// 512字节的char类型 argName存储参数类型
char argName[512] = {};
for (unsigned int j = 0; j < argumentsCount; ++j) {
method_getArgumentType(method, j, argName, 512);
NSLog(@"第%u个参数类型为:%s", j, argName);
memset(argName, '\0', strlen(argName));
}
// 512字节的char类型 returnType 存储方法返回类型
char returnType[512] = {};
method_getReturnType(method, returnType, 512);
NSLog(@"返回值类型:%s", returnType);
// type encoding
NSLog(@"TypeEncoding: %s", method_getTypeEncoding(method));
}
// 注意释放
free(methodList);
RunTime实际运用
导入你所需的头文件
发送消息
objc_msgSend(id, @selector(方法名),有参数的放在这个位置);
HOOK大法
#import "UIViewController+asdf.h"
#import <objc/runtime.h>
@implementation UIViewController (asdf)
// 加载到内存时
+ (void) load
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
// 选择器
SEL originalSEL = @selector(viewDidLoad);
SEL SwizzledSEL = @selector(hViewDidLoad);
// 方法
Method originalMethod = class_getInstanceMethod(class, originalSEL);//class_getClassMethod(class, originalSEL);备注的是获取静态方法
Method SwizzledMethod = class_getInstanceMethod(class, SwizzledSEL);//class_getClassMethod(class, SwizzledSEL);
// 方法的实现
IMP originalIMP = method_getImplementation(originalMethod);//class_getMethodImplementation(class, originalSEL);
IMP SwizzledIMP = method_getImplementation(SwizzledMethod);//class_getMethodImplementation(class, SwizzledSEL);
// 是否添加成功方法:添加了初始方法,实现内容指向目标方法体
BOOL isSuccess = class_addMethod(class, originalSEL, SwizzledIMP, method_getTypeEncoding(SwizzledMethod));
if (isSuccess) {
// 初始指向目标,那么把目标的内容指向初始
class_replaceMethod(class, SwizzledSEL, originalIMP, method_getTypeEncoding(originalMethod));
}
else{
// 没有添加成功说明已经存在,就交换
// 注意,这里交换的是IMP 实现
method_exchangeImplementations(originalMethod, SwizzledMethod);
}
});
}
- (void)hViewDidLoad{
// 是不是看着像死循环,上面交换了实现,执行这个方法的内容已经指向初始的方法体
// 那是怎么执行到这里的,是执行初始方法的IMP指向的这里,这里再指向初始的原生IMP,ok,逻辑对了,执行完原生的,去处理目标的事件了
[self hViewDidLoad];
NSLog(@"————————————————————");
}
归档解档
使用前导入 nscoding协议
- (void)encodeWithCoder:(NSCoder *)encoder{
//归档存储自定义对象
unsigned int count = 0;
//获得指向该类所有属性的指针
objc_property_t *properties = class_copyPropertyList([BDPerson class], &count);
for (int i =0; i < count; i ++) {
//获得
objc_property_t property = properties[i]; //根据objc_property_t获得其属性的名称--->C语言的字符串
const char *name = property_getName(property);
NSString *key = [NSString stringWithUTF8String:name];
// 编码每个属性,利用kVC取出每个属性对应的数值
[encoder encodeObject:[self valueForKeyPath:key] forKey:key];
}}
- (instancetype)initWithCoder:(NSCoder *)decoder{
//归档存储自定义对象
unsigned int count = 0;
//获得指向该类所有属性的指针
objc_property_t *properties = class_copyPropertyList([BDPerson class], &count);
for (int i =0; i < count; i ++) {
objc_property_t property = properties[i]; //根据objc_property_t获得其属性的名称--->C语言的字符串
const char *name = property_getName(property);
NSString *key = [NSString stringWithUTF8String:name]; //解码每个属性,利用kVC取出每个属性对应的数值
[self setValue:[decoder decodeObjectForKey:key] forKeyPath:key];
}
return self;
}