Runtime 之 API

1、关于类的一些API
//动态创建一个类(参数:父类,类名,额外的内存空间)
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)

//注册一个类(要在类注册之前添加成员变量)
void objc_registerClassPair(Class cls) 

//销毁一个类
void objc_disposeClassPair(Class cls)

//获取isa指向的Class
Class object_getClass(id obj)

//设置isa指向的Class
Class object_setClass(id obj, Class cls)

//判断一个OC对象是否为Class
BOOL object_isClass(id obj)

//判断一个Class是否为元类
BOOL class_isMetaClass(Class cls)

//获取父类
Class class_getSuperclass(Class cls)

object_getClass( ) : 获取isa指向的Class

MJPerson *person = [[MJPerson alloc] init];
[person run];
NSLog(@"%p  %p",object_getClass(person),[MJPerson class]);//打印地址结果一样
NSLog(@"%p  %p",object_getClass([MJPerson class]),[MJPerson class]);//打印地址结果不一样
object_getClass(person); //object_getClass(person)获取的是perosn对象的类,也就是[MJPerson class]
object_getClass([MJPerson class]); //object_getClass([MJPerson class])获取的是MJPerson的元类,元类是也是特殊的类,但不同于[MJPerson class]

object_setClass :设置对象isa指针指向的class

MJPerson *person = [[MJPerson alloc] init];
[person run];  //打印结果是MJPerson run
object_setClass(person, [MJCat class]); //设置person对象的isa指向MJCat
[person run];  //打印结果是MJCat run

object_isClass :用来判断入参是否是类

MJPerson *person = [[MJPerson alloc] init];
NSLog(@"%d ",object_isClass(person));  //0 : person是对象不是类
NSLog(@"%d ",object_isClass([MJPerson class])); //1 : [MJPerson Class]是类
NSLog(@"%d ",object_isClass(object_getClass(person))); //1 : object_getClass(person)获取person的类,就是MJPerson
NSLog(@"%d ",object_isClass(object_getClass([MJPerson class])));//1 : [MJPerson class]的类是元类,元类也是一种特殊的类

objc_allocateClassPair : 动态创建一个类

void run (id self,SEL _cmd){
    NSLog(@"%@ %@",self,NSStringFromSelector(_cmd));
} 

- (void)viewDidLoad {
    [super viewDidLoad];
    NSString *className = [NSString stringWithFormat:@"MJDog"];
    
    //创建类
    Class newClass = objc_allocateClassPair([NSObject class], className.UTF8String, 0);
    
    //应该在注册类之前,给类添加成员变量、方法等等
    class_addIvar(newClass, "_age", 4, 1, @encode(int));
    class_addIvar(newClass, "_weight", 4, 1, @encode(int));
    class_addMethod(newClass, @selector(run), (IMP)run,"v@:");
    
    //注册类
    objc_registerClassPair(newClass);
    id dog = [[newClass alloc] init];
    [dog run]; //打印结果: run
}

2、关于成员变量的一些API
//获取一个实例变量信息
Ivar class_getInstanceVariable(Class cls, const char *name)

//拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int *outCount)

//设置和获取成员变量的值
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)

//动态添加成员变量(已经注册的类是不能动态添加成员变量的)
BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)

//获取成员变量的相关信息
const char *ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)

class_getInstanceVariable : 获取一个实例变量的信息

Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
NSLog(@"%s %s",ivar_getName(ageIvar),ivar_getTypeEncoding(ageIvar));//打印结果: _age   i

object_setIvar : 设置成员变量的值

MJPerson *person = [[MJPerson alloc] init];
Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name");
object_setIvar(person, nameIvar, @"Jack");
object_setIvar(person, ageIvar, (__bridge id)(void *)10);//object_setIvar方法的第二个参数是id类型,得将int类型的10转成id类型
NSLog(@"person name is %@",person.name);  //person name is Jack
NSLog(@"person age is %d",person.age);    //person age is 10

class_copyIvarList : 拷贝实例变量列表
访问成员变量列表的这种方法是非常有用的:
比如在iOS项目中,看UITextField控件中的成员变量,从而窥探到_placeholderLabel @"UITextFieldLabel"这样的一个成员变量,取出该成员变量可以改变占位符的字体颜色、大小

unsigned int count;
Ivar *ivars = class_copyIvarList([MJPerson class], &count);
for (int i=0; i
3、关于属性的一些API
// 获取一个属性
objc_property_t class_getProperty(Class cls, const char *name)

// 拷贝属性列表(最后需要调用free释放)
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)

// 动态添加属性
BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                  unsigned int attributeCount)

// 动态替换属性
void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,
                      unsigned int attributeCount)

// 获取属性的一些信息
const char *property_getName(objc_property_t property)
const char *property_getAttributes(objc_property_t property)

4、关于方法的一些API
// 获得一个实例方法、类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

// 方法实现相关操作
IMP class_getMethodImplementation(Class cls, SEL name) 
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementations(Method m1, Method m2) 

// 拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsigned int *outCount)

// 动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

// 动态替换方法
IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

// 获取方法的相关信息(带有copy的需要调用free去释放)
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char *method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)

// 选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)

// 用block作为方法实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)

class_replaceMethod : 动态替换方法

void myrun(){
    NSLog(@"my ___ run");
}


@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    MJPerson *person = [[MJPerson alloc] init];
    
    //1、替换方法:在外部实现一个方法myrun用于替换原有方法run
    class_replaceMethod([MJPerson class], @selector(run), (IMP)myrun, "v");
    
    //2、替换方法:直接使用block来替换掉原有方法run
    class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
        NSLog(@"this is a block");
    }), "v");
   
    [person run];

method_exchangeImplementations : 交换方法

//交换方法:实例方法、类方法都可以交换
Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
method_exchangeImplementations(testMethod, runMethod);
[person run];

一般在实际操作中,我们很少交换自己写的方法,它的价值主要体现在交换第三方库或者系统方法。
比如说要实现拦截所有的按钮点击事件,我们可以这么做:
添加UIControl的分类(因为UIButton继承自UIControl):

#import "UIControl+Ext.h"
#import 

@implementation UIControl (Ext)

+(void)load{
    //交换方法其实就是交换掉Method里面的方法实现IMP
    Method beforeMethod = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method afterMethod = class_getInstanceMethod(self, @selector(exchange_sendAction:to:forEvent:));
    method_exchangeImplementations(beforeMethod, afterMethod);
}

//UIButton中监听点击事件主要是sendAction:to:forEvent:实现
//所以使用下面的方法exchange_XXX 去替换 sendAction:to:forEvent:
-(void)exchange_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event{
    //调用系统原来的实现要调用exchange_XXX,因为已经交换过方法了
    [self exchange_sendAction:action to:target forEvent:event];
    if ([self isKindOfClass:[UIButton class]]) {
        NSLog(@"这里拦截了所有的按钮点击事件");
    }
}
@end

之前我们了解过一个方法Method中包含name、types、imp。
其实method_exchangeImplementations底层就是替换了两个方法的方法实现imp,其余元素不变。

void method_exchangeImplementations(Method m1, Method m2)
{
    if (!m1  ||  !m2) return;

    rwlock_writer_t lock(runtimeLock);
    
    //可以看出,主要就是替换了两个方法的方法实现imp
    IMP m1_imp = m1->imp;
    m1->imp = m2->imp;
    m2->imp = m1_imp;

    //清除方法缓存
    flushCaches(nil);

    updateCustomRR_AWZ(nil, m1);
    updateCustomRR_AWZ(nil, m2);
}

你可能感兴趣的:(Runtime 之 API)