Runtime大型讲解

本文详细介绍了Objective-C运行时机制,包括如何通过Runtime API动态地获取和修改类、成员变量、属性及方法等内容,并提供了丰富的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值