iOS源码解析:runtime runtime的API

栏目: IOS · 发布时间: 5年前

内容简介:runtime的API有很多,内容很丰富,下面按照类方法,成员变量相关方法,属性相关方法,方法相关等进行说明。与类相关的API有下列这些:首先看后面几个API。

runtime的API有很多,内容很丰富,下面按照类方法,成员变量相关方法,属性相关方法,方法相关等进行说明。

Runtime API01 - 类

与类相关的API有下列这些:

    //动态创建一个类(参数:父类,类名,额外的内存空间)
    Class objc_allocateClassPair(Class  _Nullable __unsafe_unretained superclass, const char * _Nonnull name, size_t extraBytes);    
    //注册一个类(要在类注册之前添加成员变量)
    void objc_registerClassPair(Class  _Nonnull __unsafe_unretained cls);    
    //销毁一个类
    void objc_disposeClassPair(Class  _Nonnull __unsafe_unretained cls);    
    //获取isa指向的Class
    Class object_getClass(id  _Nullable obj);    
    //设置isa指向的Class
    Class object_setClass(id  _Nullable obj, Class  _Nonnull __unsafe_unretained cls);    
    //判断一个OC对象是否为Class
    BOOL object_isClass(id  _Nullable obj);    
    //判断一个Class是否为元类
    BOOL class_isMetaClass(Class  _Nullable __unsafe_unretained cls);    
    //获取父类
    CLass class_getSuperclass(Class  _Nullable __unsafe_unretained cls);

首先看后面几个API。

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

这个API是获取传入对象的isa指针指向的对象,传入的是实例对象则返回类对象,传入的是类对象则返回元类对象。

 //设置isa指向的Class
    Class object_setClass(id  _Nullable obj, Class  _Nonnull __unsafe_unretained cls);

这个API是改变一个对象的isa指针的指向,比如下面代码:

Person *girl = [[Person alloc] init];
    object_setClass(girl, [Student class]);
    NSLog(@"%@", [girl class]);

调用 object_setClass() 使girl这个实例对象的isa指针指向Student类对象,所以打印的结果是:

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

这个API用来判断传入的对象是否为类对象,由于元类对象也是类对象的一种,所以这里实际是判断传入的对象是否为类对象或元类对象,实例代码如下:

  Person *girl = [[Person alloc] init];
    Class girlClass = [girl class];
    Class girlMetaClass = object_getClass(girlClass);
    NSLog(@"%d, %d, %d", object_isClass(girl), object_isClass(girlClass), object_isClass(girlMetaClass));

打印结果如下:

0, 1, 1
//判断一个Class是否为元类
    BOOL class_isMetaClass(Class  _Nullable __unsafe_unretained cls);

这个API用来判断传入的类对象是否是元类对象,实例代码如下:

  Person *girl = [[Person alloc] init];
    Class girlClass = [girl class];
    Class girlMetaClass = object_getClass(girlClass);
    NSLog(@"%d, %d", class_isMetaClass(girlClass), class_isMetaClass(girlMetaClass));

打印结果:

0, 1
//获取父类
    CLass class_getSuperclass(Class  _Nullable __unsafe_unretained cls);

这个API是传入一个类对象,获取一个父类对象,例如下面的代码:

 Person *girl = [[Person alloc] init];
    Class girlClass = [girl class];
    NSLog(@"%@", class_getSuperclass(girlClass));

打印结果是:

NSObject

接下里再来看前面的三个API:

//动态创建一个类(参数:父类,类名,额外的内存空间)
    Class objc_allocateClassPair(Class  _Nullable __unsafe_unretained superclass, const char * _Nonnull name, size_t extraBytes);

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

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

这三个API结合起来动态创建和销毁一个类。

第一个API中的参数分别是要创建的这个类要继承自哪个父类,类名,额外的内存空间(一般传0)。

所以可以像下面这样动态创建一个类:

Class teacherClass = objc_allocateClassPair([Person class], "Teacher", 0);

这就是创建了一个继承自Person类的子类,名称为Teacher。

然后我们可以使用下列API给新创建的类添加成员变量:

BOOL
class_addIvar(Class _Nullable cls, const char * _Nonnull name, size_t size, 
              uint8_t alignment, const char * _Nullable types)

这里的参数中,第一个参数是要传入类对象,第二个参数是成员变量名,第三个是成员变量占的内存空间,第四个是内存对齐,第五个是成员变量的类型,我们可以这样添加成员变量:

//创建了_age和_weight这两个成员变量
    class_addIvar(teacherClass, "_age", 4, 1, @encode(int));
    class_addIvar(teacherClass, "_weight", 4, 1, @encode(int));

我们还可以使用下列API给新创建的类添加方法:

BOOL
class_addMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp, 
                const char * _Nullable types)

可以这样来添加:

Method method = class_getInstanceMethod([self class], @selector(teach));
    IMP imp = method_getImplementation(method);
    class_addMethod(teacherClass, @selector(teach), imp, "v16@0:8");

这样一来就为新创建的Teacher类添加了成员变量和方法。

添加完成员变量和方法后,我们再去注册这个类:

objc_registerClassPair(teacherClass);

那么我们怎么去给成员变量赋值和取值呢?由于没有set和get方法,所以肯定是不能直接取值和赋值,需要通过KVC来取值和赋值:

 id teacher = [[teacherClass alloc] init];
    [teacher setValue:@10 forKey:@"_age"];
    [teacher setValue:@20 forKey:@"_weight"];

    [teacher teach];
    NSLog(@"%@, %@", [teacher valueForKey:@"_age"], [teacher valueForKey:@"_weight"]);

Runtime API02 - 成员变量

这一部分主要讲与成员变量相关的API,主要有下列这些:

//获取一个成员变量
    Ivar class_getInstanceVariable(Class  _Nullable __unsafe_unretained cls, const char * _Nonnull name);

    //拷贝成员变量列表
    Ivar *class_copyIvarList(Class  _Nullable __unsafe_unretained cls, unsigned int * _Nullable outCount);

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

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

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

下面我们一个一个来分析:

//获取一个成员变量
    Ivar class_getInstanceVariable(Class  _Nullable __unsafe_unretained cls, const char * _Nonnull name);

这个就是传入类对象和成员变量的名字,返回一个Ivar类型的成员变量:

Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
 //拷贝成员变量列表
    Ivar *class_copyIvarList(Class  _Nullable __unsafe_unretained cls, unsigned int * _Nullable outCount);

这个API是返回这个类的所有成员变量的一个列表。这个API可以和下面这个API结合使用来打印类的所以成员变量:

//获取成员变量的相关信息
    const char *ivar_getName(Ivar  _Nonnull v);
    const char *ivar_getTypeEncoding(Ivar  _Nonnull v);
  unsigned int count;
    Ivar *ivarList = class_copyIvarList([Person class], &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
       const char *ivarName = ivar_getName(ivar);
        NSLog(@"%s", ivarName);
    }
    free(ivarList);

这样就会打印类的所有成员变量的值

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

这是给成员变量设值和取值,用法如下:

 Person *person = [[Person alloc] init];
    Ivar nameIvar = class_getInstanceVariable([Person class], "_name");
    object_setIvar(person, nameIvar, @"xiaoli");
    id name = object_getIvar(person, nameIvar);
//动态添加成员变量(已经注册的类不能动态添加成员变量)
    BOOL class_addIvar(Class  _Nullable __unsafe_unretained cls, const char * _Nonnull name, size_t size, uint8_t alignment, const char * _Nullable types);

给类动态添加成员变量,这个之前已经使用过,只是要说明的是,不能在类注册后去动态添加成员变量,因为成员变量是存放在class_ro_t结构体中的,是不能修改的,因此一旦类注册成功后,class_ro_t结构体就不能修改,也就不能再添加成员变量。

简单应用

下面讲一个利用这类API的实际应用。首先拖拽一个UITextField到Main.storyboard,命名为textField。然后设置这个输入框的占位符:

self.textField.placeholder = @"请输入文字";

然后我们运行代码,发现占位文字显示出来了,但是是灰色,这种占位文字的颜色并不是我想要的,我想要改变占位文字的颜色,但是我在UITextField的API中又没有找到相关的API,怎么办呢?我想到这个占位文字这里的设计应该是用了一个UILabel,如果我真的找到了这个label,那么我就可以利用KVC去修改其textColor属性,我利用runtime的API打印一下其成员变量,看看有没有我要找的这个label:

 unsigned int count;
    Ivar *ivarList = class_copyIvarList([self.textField class], &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivarList[i];
       const char *ivarName = ivar_getName(ivar);
        NSLog(@"%s", ivarName);
    }
    free(ivarList);

打印结果:


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员成长的烦恼

程序员成长的烦恼

吴亮、周金桥、李春雷、周礼 / 华中科技大学出版社 / 2011-4 / 28.00元

还在犹豫该不该转行学编程?还在编程的道路上摸爬滚打?在追寻梦想的道路上你并不孤单,《程序员成长的烦恼》中的四位“草根”程序员也曾有过类似的困惑。看看油田焊接技术员出身的周金桥是如何成功转行当上程序员的,做过钳工、当过外贸跟单员的李春雷是如何自学编程的,打小在486计算机上学习编程的吴亮是如何一路坚持下来的,工作中屡屡受挫、频繁跳槽的周礼是如何找到出路的。 《程序员成长的烦恼》记录了他们一步一......一起来看看 《程序员成长的烦恼》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

在线进制转换器
在线进制转换器

各进制数互转换器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试