iOS开发-浅解runtime

版权所有,禁止匿名转载;禁止商业使用。

1.什么是runtime

runtime是苹果封装的底层C语言api,包含许多功能强大的C语言数据结构和函数.实际上我们平时所编写的OC代码,底层都是基于runtime实现的.也就是说我们平时编写的代码,最终都转换成了底层的runtime代码.

runtime有什么作用呢?
1> 能动态产生一个类、一个成员变量、一个方法
2> 能动态修改一个类、一个成员变量、一个方法
3> 能动态删除一个类、一个成员变量、一个方法

2.如何使用runtime

在我们实际工作中也许很少直接使用runtime,但我却时时刻刻间接的和runtime打交道.那么在什么情况下我们需要直接使用runtime呢,以下是我列举的一些常用方式.
1.枚举一个类里面的所有方法,变量和属性.不管是自定义的还是系统自带的都可以获取.
2.交换类或者对象中的方法名.

3.常见的函数、头文件

#import <objc/runtime.h> : 成员变量、类、方法

Ivar * class_copyIvarList : 获得某个类内部的所有成员变量
Method * class_copyMethodList : 获得某个类内部的所有方法
Method class_getInstanceMethod : 获得某个实例方法(对象方法,减号-开头)
Method class_getClassMethod : 获得某个类方法(加号+开头)
method_exchangeImplementations : 交换2个方法的具体实现

#import<objc/message.h> : 消息机制

objc_msgSend(….)

3.runtime实例分析

自定义一个类MRPerson,该类中含有两个属性,一个方法:

//1.导入头文件 #import<objc/message.h> //2.属性 @property (strong, nonatomic) NSString *name; @property (assign, nonatomic) NSInteger age; //3.方法 -(void)run; //- (void)run{ //    NSLog(@"-------run--------"); //}

1.使用objc_msgSend(…)发消息

我们平时常常说调用OC类中的某个方法,严格来说这样并不正确.而应该说成向某个对象发送消息.

-(void)useObjcSendMessage {     MRPerson* person = [[MRPerson alloc]init];     //[person setAge:10];     objc_msgSend(person, @selector(setAge:), 10);      printf("%d",(int)objc_msgSend(person, @selector(age))); }

在这个方法中,objc_msgSend(person, @selector(setAge:), 10)
就相当于[person setAge:10].用通俗的话说,就是向对象person,发送一个消息setAge:,该消息中含有一个参数10.

其实我们平时调用OC方法底层都是通过objc_msgSend进行消息发送的.

2.获取类中的成员变量

1.Ivar *class_copyIvarList(Class cls, unsigned int *outCount)
这个方法就是用来获取一个类中所有的成员变量的:
返回值:一个指向Ivar数组的指针,里面保存的就是类中所有的变量
第一个参数:要从哪个类中遍历所有的变量.
第二个参数:该类中变量的个数

2.const char *ivar_getName(Ivar v)
返回值:变量的名称,一个常量字符串
参数:对应的变量

3.const char *ivar_getTypeEncoding(Ivar v)
返回值:变量的类型
参数:对应的变量

-(void)getIvar {     unsigned int outCount= 0;     Ivar* ivar = class_copyIvarList([MRPerson class], &outCount);      for (int i = 0 ; i < outCount; i++) {         const char* name =  ivar_getName(ivar[i]);         const char* type = ivar_getTypeEncoding(ivar[i]);         printf("%d %s %s\n",i,name,type);     } }

输出:

0 _name @"NSString" 1 _age i

3.交换方法Swizzing

交换方法我们需要使用以下三个函数:

  • Method class_getInstanceMethod : 获得某个实例方法(对象方法,减号-开头)
  • Method class_getClassMethod : 获得某个类方法(加号+开头)
  • method_exchangeImplementations : 交换2个方法的具体实现

在iOS中通过下标取出NSArray中的成员,如果下标越界,程序就会崩溃.
现在我想自定义一个方法- (id)mr_objectAtIndex:(NSUInteger)index来进行下标访问,当越界的时候会有提示,并且返回nil.

给NSArray写一个分类,分类实现如下:

#import <Foundation/Foundation.h> #import <objc/runtime.h>  @implementation NSArray (MR) //__NSArrayI objectAtIndex: /**  *  只要类被装载到内存中,就会调用1次  */ +(void)load {      Method one = class_getInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(objectAtIndex:));     Method two =  class_getInstanceMethod(NSClassFromString(@"__NSArrayI"), @selector(mr_objectAtIndex:));      method_exchangeImplementations(one, two); } - (id)mr_objectAtIndex:(NSUInteger)index {     if (index >= self.count) {         NSLog(@"数组越界");         return nil;     } else{         //注意这里:由于方法已经被交换了,这里相当于[self objectAtIndex:index] 并不会死循环.         return [self mr_objectAtIndex:index];     } } @end

在ViewController.m文件中实现以下方法,并且调用:

-(void)swizzlingMethod {     NSArray *array = @[@(1),@(2),@(3)];     NSLog(@"%@",array[3]); }

输出结果:

2015-09-17 19:59:54.331 runtime机制[21741:1450284] 数组越界 2015-09-17 19:59:54.332 runtime机制[21741:1450284] (null)

下面的程序是我写的NSObject的分类,大家可以通过调用下面两个方法来分别实现类方法的交换,对象方法的交换.

//类方法 + (void)swizzleClassMethod:(Class)class originSelector:(SEL)originSelector otherSelector:(SEL)otherSelector //对象方法 + (void)swizzleInstanceMethod:(Class)class originSelector:(SEL)originSelector otherSelector:(SEL)otherSelector 
#import <objc/runtime.h>  @implementation NSObject(Extension) + (void)swizzleClassMethod:(Class)class originSelector:(SEL)originSelector otherSelector:(SEL)otherSelector {     Method otherMehtod = class_getClassMethod(class, otherSelector);     Method originMehtod = class_getClassMethod(class, originSelector);     // 交换2个方法的实现     method_exchangeImplementations(otherMehtod, originMehtod); }  + (void)swizzleInstanceMethod:(Class)class originSelector:(SEL)originSelector otherSelector:(SEL)otherSelector {     Method otherMehtod = class_getInstanceMethod(class, otherSelector);     Method originMehtod = class_getInstanceMethod(class, originSelector);     // 交换2个方法的实现     method_exchangeImplementations(otherMehtod, originMehtod); } @end  @implementation NSArray(Extension) + (void)load {     [self swizzleInstanceMethod:NSClassFromString(@"__NSArrayI") originSelector:@selector(objectAtIndex:) otherSelector:@selector(hm_objectAtIndex:)]; }  - (id)hm_objectAtIndex:(NSUInteger)index {     if (index < self.count) {         return [self hm_objectAtIndex:index];     } else {         return nil;     } }  @end  @implementation NSMutableArray(Extension) + (void)load {     [self swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originSelector:@selector(addObject:) otherSelector:@selector(hm_addObject:)];     [self swizzleInstanceMethod:NSClassFromString(@"__NSArrayM") originSelector:@selector(objectAtIndex:) otherSelector:@selector(hm_objectAtIndex:)]; }  - (void)mr_addObject:(id)object {     if (object != nil) {         [self hm_addObject:object];     } }  - (id)mr_objectAtIndex:(NSUInteger)index {     if (index < self.count) {         return [self hm_objectAtIndex:index];     } else {         return nil;     } } @end
0 0