浅谈iOS中runtime

一、runtime机制

runtime: 指一个程序在运行(或在被执行)的状态。也就是说,当你打开一个程序使它在电脑上运行的时候,那个程序就是处于运行时刻。在一些编程语言中,把某些可以重用的程序或者实例打包或者重建成为“运行库”。这些实例可以在他们运动的时候被连接或者被任何程序调用。

objective-C中runtime:是一套比较底层的纯C语言API,属于一个C语言库,包含了很多底层的C语言API。

  • 在我们平时编写的OC代码在程序运行过程中都会转化成runtime的C语言代码执行,例如[target doSomething];, 会被转化成objc_msgSend(target,@selector(doSomething));

  • OC中一切都被设计成了对象,我们都知道一个类被初始化成一个实例,这个实例是一个对象,实际上一个类的本质上也是以一个对象,在runtime中用结构体表示。
    (见上一篇【类的本身也是一个对象—-isa指针详解】

  • 相关的定义:

/// 描述类中的一个方法
typedef struct objc_method *Method;

/// 实例变量
typedef struct objc_ivar *Ivar;

/// 类别Category
typedef struct objc_category *Category;

/// 类中声明的属性
typedef struct objc_property *objc_property_t;

二、runtime的应用

1. 获取列表

有时候我们会需要获取当前类中每个属性的名字(比如字典转模型,字典的key和模型对象的属性名字不匹配)。
我们可以通过runtime的一些列方法获取类的一些信息(包括属性列表,方法列表,成员变量列表和遵循的协议列表)。

unsigned int count;
    //获取属性列表
    objc_property_t *propertyList = class_copyPropertyList([self class], &count);
    for (unsigned int i=0; iconst char *propertyName = property_getName(propertyList[i]);
        NSLog(@"property---->%@", [NSString stringWithUTF8String:propertyName]);
    }

    //获取方法列表
    Method *methodList = class_copyMethodList([self class], &count);
    for (unsigned int i; iNSLog(@"method---->%@", NSStringFromSelector(method_getName(method)));
    }

    //获取成员变量列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (unsigned int i; iconst char *ivarName = ivar_getName(myIvar);
        NSLog(@"Ivar---->%@", [NSString stringWithUTF8String:ivarName]);
    }

    //获取协议列表
    __unsafe_unretained Protocol **protocolList = class_copyProtocolList([self class], &count);
    for (unsigned int i; iconst char *protocolName = protocol_getName(myProtocal);
        NSLog(@"protocol---->%@", [NSString stringWithUTF8String:protocolName]);
    }

注:调用这些获取列表的方法别忘记导入头文件#import

2.方法调用

主要依靠isa指针((见上一篇【类的本身也是一个对象—-isa指针详解】)

  • 如果用实例对象调用实例方法,会到实例的isa指针指向的对象(也就是类对象)操作。
  • 如果调用的是类方法,就会到类对象的isa指针指向的对象(也就是元类对象)中操作。

1.首先,在相应操作的对象中的缓存方法列表中找调用的方法,如果找到,转向相应的实现并执行。
2.如果没找到,在相应操作的对象中的方法列表中找调用的方法,如果找到,转向相应实现执行。
3.如果没找到,去父类指针所指向的对象中执行1,2.
4.以此类推,如果一直到根类还没找到,转向拦截调用。
5.如果没有重写拦截调用的方法,程序报错。

3.拦截调用

拦截调用即在找不到调用的方法程序崩溃之前,可以通过重写以下几个方法来处理:

+ (BOOL)resolveClassMethod:(SEL)sel;
+ (BOOL)resolveInstanceMethod:(SEL)sel;
//后两个方法需要转发到其他的类处理
- (id)forwardingTargetForSelector:(SEL)aSelector;
- (void)forwardInvocation:(NSInvocation *)anInvocation;

4.动态添加方法

增加函数:class_addMethod
四个参数的意思分别是:

  1. Class cls 给哪个类添加方法,本例中是self
  2. SEL name 添加的方法,本例中是重写的拦截调用传进来的selector。
  3. IMP imp 方法的实现,C方法的方法实现可以直接获得。如果是OC方法,可以用+(IMP)instanceMethodForSelector:(SEL)aSelector;获得方法的实现。
  4. “v@:*”方法的签名,代表有一个参数的方法。

5.关联对象 (添加属性)

///设置关联对象
objc_setAssociatedObject

四个参数的意思分别是:
1. id object 给谁设置关联对象
2. const void *key 关联对象唯一的key,获取时会用到
3. id value 关联对象
4. objc_AssociationPolicy 关联策略(属性修饰符枚举 )

//获取关联对象
objc_getAssociatedObject

两个参数分别是:

  1. id object 获取谁的关联对象
  2. const void *key 根据这个唯一的key获取关联对象

6.方法交换

方法交换,即将两个方法的实现交换。例如,将A方法和B方法交换,调用A方法的时候,就会执行B方法中的代码,反之亦然。

交换两个方法的实现:method_exchangeImplementations.

替换类方法的定义:class_replaceMethod

设置一个方法的实现:method_setImplementation.

你可能感兴趣的:(OC语言,编程基础)