Runtime简介(一)

一、首先,从 runtime.h头文件中找到对 class 与 object 的定义

 typedef struct objc_class *Class;


 struct objc_object {
 Class isa;
 };
 typedef struct objc_object *id;

由此可见,Class是一个指向objc_class结构体的指针,而id是一个指向objc_object结构体的指针,其成员isa是一个指向objec_class结构体的指针。

二、下面我们再看看头文件中关于objc_class的定义

 truct objc_class {

 Class isa; // 指向metaclass

 Class super_class ; // 指向其父类

 const char *name ; // 类名

 long version ; // 类的版本信息,初始化默认为0,可以通过runtime函数class_setVersion和class_getVersion进行修改、读取

 long info; // 一些标识信息,如CLS_CLASS (0x1L) 表示该类为普通 class ,其中包含对象方法和成员变量;CLS_META (0x2L) 表示该类为 metaclass,其中包含类方法;

 long instance_size ; // 该类的实例变量大小(包括从父类继承下来的实例变量);

 struct objc_ivar_list *ivars; // 用于存储每个成员变量的地址

 struct objc_method_list **methodLists ; // 方法列表,与 info 的一些标志位有关,如CLS_CLASS (0x1L),则存储对象方法,如CLS_META (0x2L),则存储类方法;

 struct objc_cache *cache; // 缓存,一种优化,调用过的方法存入缓存列表,下次调用先找缓存

 struct objc_protocol_list *protocols; // 存储该类遵守的协议  协议列表

 }

由此可见,类比对象的结构体中多了众多的成员,下面详细介绍下objec_class中各成员:

isa:objec_object(对象)中isa指针指向的类结构称为class(也就是该对象所属的类),其中存放着普通成员变量与对象方法 (“-”开头的方法);然而此处isa指针指向的类结构称为metaclass,其中存放着static类型的成员变量与static类型的方法 (“+”开头的方法)。

super_class: 指向该类的父类的指针,如果该类是根类(如NSObject或NSProxy),那么super_class就为NULL。

1、当我们调用某个对象的对象方法时,它会首先在自身isa指针指向的类(class)methodLists中查找该方法,如果找不到则会通过class的super_class指针找到其父类,然后从其methodLists中查找该方法,如果仍然找不到,则继续通过 super_class向上一级父类结构体中查找,直至根class;

2、当我们调用某个类方法时,它会首先通过自己的isa指针找到metaclass,并从其methodLists中查找该类方法,如果找不到则会通过metaclass的super_class指针找到父类的metaclass结构体,然后从methodLists中查找该方法,如果仍然找不到,则继续通过super_class向上一级父类结构体中查 找,直至根metaclass;

三.我们可以通过runtime的一系列方法获取类的一些信息(包括属性列表,方法列表,成员变量列表,和遵循的协议列表)
首先创建一个Person类。.h和.m文件代码如下:

#import <Foundation/Foundation.h>

@interface Person : NSObject
-(void)ButtonClicked1;

+(void)ButtonClicked2;


@end
#import "Person.h"

@interface Person (){

    NSString  *_age;
}


@property (nonatomic, strong)NSString *name;

@property (nonatomic, assign)int no;


@end

@implementation Person
-(void)ButtonClicked1{

    NSLog(@"ButtonClicked1");

}
+(void)ButtonClicked2{

    NSLog(@"ButtonClicked2");

}

- (id)init
{
    if (self = [super init]) {
        self.name = @"wengzilin";

       _age = @"27";

    }
    return self;
}


- (NSString *)description
{
    NSLog(@"name:%@, age:%@", self.name, _age);
    return [NSString stringWithFormat:@"name:%@, age:%@", self.name, _age];
}



@end

然后在ViewController中,引入:

#import <objc/runtime.h>

#import "Person.h"

1.获取一个类的所有属性:

unsigned int count = 0 ;
    //获取属性列表
    objc_property_t *propertyList = class_copyPropertyList([Person class], &count);
    for (unsigned int i=0; i<count; i++) {
        const char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
    }
/*   打印结果:
 2016-03-08 14:57:21.471 RunTimeDemo1[3108:936300] property---->name
 2016-03-08 14:57:21.471 RunTimeDemo1[3108:936300] property---->no

 */

2.获取成员变量列表

Person *per = [[Person alloc]init];
     NSLog(@"before runtime:%@",[per description]);

    Ivar *ivarLists = class_copyIvarList([Person class], &count);
    for (unsigned int i; i<count; i++) {
        Ivar myIvar = ivarLists[i];
        const char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }
/*   打印结果:
     2016-03-08 15:23:11.702 RunTimeDemo1[3731:1091064] before runtime:name:wengzilin, age:27
     2016-03-08 15:23:11.702 RunTimeDemo1[3731:1091064] Ivar---->_age
     2016-03-08 15:23:11.702 RunTimeDemo1[3731:1091064] Ivar---->_no
     2016-03-08 15:23:11.702 RunTimeDemo1[3731:1091064] Ivar---->_name     */

    /*我们知道,正常情况下,在这个控制器里面是访问不到Person中的私有变量和属性的,但是对于runtime,不管是私有的还是公有的成员变量和属性,runtime都可以将之访问并修改掉,例如:修改掉person的私有成员变量age的值   
     为什么会有上面的输出结果,因为@property会做三份工作:
    1.生成一个带下划线的成员变量
    2.生成这个成员变量的get方法
    3.生成这个成员变量的set方法
    因此会输出三个成员变量_no、_age和_name。需要注意的是属性名是不带下划线的,和定义时的名字一样。因此可以说:ivarList可以获取到@property关键字定义的属性 ,而propertyList不可以获取到成员变量。也就是:使用ivarList是可以将所有的成员变量和属性都获取的。
   */

//修改person的私有成员变量的值

Ivar age = ivarLists[0];
    object_setIvar(per, age, @"20");
       NSLog(@"after runtime:%@",[per description]);

/*
打印结果:
2016-03-08 15:23:11.702 RunTimeDemo1[3731:1091064] after runtime:name:wengzilin, age:20

 即成功的将person的私有成员变量的值给改变了
 */

3.获取方法列表

 unsigned int count = 0;
    //获取方法列表   遍历某个类所有的方法
    Method *methodList = class_copyMethodList([Person class], &count);//所有在.m文件显式实现的方法都会被找到
    for (unsigned int i; i<count; i++) {


        SEL name = method_getName(methodList[i]);//读取一个Method类型的变量,输出我们在上层中很熟悉的SEL

        NSString *methodName = [NSString stringWithCString:sel_getName(name) encoding:NSUTF8StringEncoding];
        NSLog(@"member method:%@", methodName);
/*   打印结果:
     2016-03-08 15:32:42.588 RunTimeDemo1[3883:1144407] member method:ButtonClicked1
     2016-03-08 15:32:42.588 RunTimeDemo1[3883:1144407] member method:no
     2016-03-08 15:32:42.588 RunTimeDemo1[3883:1144407] member method:setNo:
     2016-03-08 15:32:42.588 RunTimeDemo1[3883:1144407] member method:.cxx_destruct
     2016-03-08 15:32:42.588 RunTimeDemo1[3883:1144407] member method:description
     2016-03-08 15:32:42.589 RunTimeDemo1[3883:1144407] member method:name
     2016-03-08 15:32:42.589 RunTimeDemo1[3883:1144407] member method:setName:
     2016-03-08 15:32:42.589 RunTimeDemo1[3883:1144407] member method:init


     */
     不但能得到普通方法,还能将生成的get方法和set方法也打印出来

4.获取协议列表

 __unsafe_unretained Protocol **protocolList = class_copyProtocolList([Person class], &count);
    for (unsigned int i; i<count; i++) {
        Protocol *myProtocal = protocolList[i];
        const char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值