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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值