iOS 运行时Runtime

Runtime能做非常非常多的事情,但是不能为了使用runtime而使用,因为使用runtime会使代码的阅读性降低,使用也不方便。只能在不得已的情况下使用。

以下介绍几个用法。

导入#import <objc/message.h> 或者 #import <objc/runtime.h>

一、发消息objc_msgSend();

a、获取一个类 objc_getClass();  

b、注册一个方法 sel_registerName();

c、获取一个方法 sel_getUid();

//创建一个对象
    
    //NSObject * obj = [NSObject alloc];
    //id obj = objc_msgSend([NSObject class], @selector(alloc));
    //发生消息,调用alloc方法,和上面两句代码都是等价的,他的作用都是一样
    id obj = objc_msgSend(objc_getClass("NSObject"), sel_registerName("alloc"));
    
    //obj = [obj init];
    //obj = objc_msgSend(obj, @selector(init));
    //发生消息,调用init方法,和上面两句代码都是等价的,他的作用都是一样
    obj =  objc_msgSend(obj, sel_registerName("init"));/*
     objc_getClass("类名") 获取一个类
     sel_registerName("方法名") 注册一个方法
     */

 

二、方法交换

这里拿UIImage为例,添加一个UIImage的分类UIImage+jh.m

重新类方法+load

+(void)load{
    //根据类获取类方法imageNamed
    Method imageNamedMethod = class_getClassMethod(self, @selector(imageNamed:));
    //根据类获取类方法xg_imageNamed
    Method xg_imageNamedMethod = class_getClassMethod(self, @selector(xg_imageNamed:));
    //交换方法
    method_exchangeImplementations(imageNamedMethod, xg_imageNamedMethod);
}

写一个交换的方法xg_imageName:

+(UIImage *)xg_imageNamed:(NSString *)name{
    //这里调用xg_imageNamed: 不是调用当前这个方法,因为我们在load中已经交换了imageNamed:这个方法,所以,这里调用xg_imageNamed:就会调用imageNamed:方法。
    UIImage * image = [UIImage xg_imageNamed:name];
    if (image) {
        NSLog(@"%@图片加载成功!",name);
    }else{
        NSLog(@"%@图片加载失败!",name);
    }
    return image;
};

所以我们在使用系统方法imageNamed:的时候就会调用xg_imageNamed:这个方法。

UIImage * image = [UIImage imageNamed:@"1.png"];

这样能够判断一个图片是否加载成功,而不需要修改系统方法,也不需要修改其他代码,方便扩展和开发。

 

三、动态添加属性

比如我需要给系统的NSObject类添加一个属性,那么大家都会想到使用分类添加一个属性,然后手动写setting和getting方法。

使用runtime也是一样,不过在写setting和getting方法的时候不太一样,使用runtime会方便非常多。比如加一个name的方法

添加一个NSObject+pro分类

NSObject+pro.h

#import <Foundation/Foundation.h>

@interface NSObject (pro)

/**
 添加一个name属性
 */
@property NSString * name;

@end

NSObject+pro.m

#import "NSObject+pro.h"
#import <objc/message.h>

@implementation NSObject (pro)

//写setting方法
-(void)setName:(NSString *)name{
    //设置一个关联 ,因为我们所有的属性其实就是一个关联
    //objc_setAssociatedObject(给哪个对象添加关联, @"关联名称", 关联的值, 修饰(我这里name是一个字符串,所有使用了copy));
    objc_setAssociatedObject(self, @"name", name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

//写getting方法
-(NSString *)name{
    return objc_getAssociatedObject(self, @"name");
}

@end

使用的时候就可以直接.name了

NSObject * object = [[NSObject alloc]init];
    object.name = @"123";
    NSLog(@"name:%@",object.name);

 

四、动态添加方法

创建一个Person : NSObject 类

在viewDidLoad方法执行以下代码

Person * p = [[Person alloc]init];
    //这里调用eat:方法,但是在person中并没有这个方法,而在运行的使用动态创建
    [p performSelector:@selector(eat:) withObject:@"apple"];
    //这里调用run方法并且返回一个结果,但是在person中并没有这个方法,而在运行的使用动态创建
    NSNumber * i = [p performSelector:@selector(run)];
    NSLog(@"p run :%@",i);

那么来看看是怎么动态添加

person.h文件是空的,什么都没有。

#import <Foundation/Foundation.h>

@interface Person : NSObject

@end

主要看person.m文件

#import "Person.h"
#import <objc/message.h>

@implementation Person

//eat:方法的实现
void tEat(id self , SEL _cmd ,NSString * e){
    NSLog(@"Person eat :%@",e);
}

//run方法的实现
NSNumber * tRun(id self , SEL _cmd){
    return @100;
}

//但运行的时候,找不到某个方法就会执行
+(BOOL)resolveInstanceMethod:(SEL)sel{
    //判断是不是找不到eat:方法
    if (sel == NSSelectorFromString(@"eat:")) {
        /*
         添加方法
         */
        //class :给谁(那个类)添加方法
        //SEL :添加什么方法
        //imp :方法的实现 -》函数 - 》 函数入口 - 》 函数名
        //type :方法类型 方法类型怎么描述查看开发文档 Objective-C Runtime Programming Guide > Type Encodings.可以按住option键点击下面class_addMethod()函数,在方法说明里面用“不是黑色的字体”描述,可以直接跳转到对应说明
        class_addMethod(self, sel, (IMP)tEat, "v@:@");
        return YES;
    }
    //判断是不是找不到run 方法
    if (sel == NSSelectorFromString(@"run")) {
        class_addMethod(self, sel, (IMP)tRun, "@@:");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

@end

 

五、获取成员变量和类型

添加一个分类 NSObject+runtime

NSObject+runtime.h文件

#import <Foundation/Foundation.h>

@interface NSObject (runtime)

//获取所有的成员变量
+(instancetype)getAllIvar;

@end

NSObject+runtime.m文件

#import "NSObject+runtime.h"
#import <objc/message.h>

@implementation NSObject (runtime)

+(instancetype)getAllIvar{
    id obj = [[self alloc]init];
    unsigned int outCount = 0;
    //获取成员变量列表
    Ivar * varList =  class_copyIvarList(self, &outCount);
    for (int i = 0; i< outCount; i++) {
        Ivar ivar = varList[i];
        //获取成员变量名
        NSString * ivarName = [NSString stringWithUTF8String:ivar_getName(ivar)];
        ivarName = [ivarName substringFromIndex:1];
        NSLog(@"成员变量名称:%@",ivarName);
        
        //给属性赋值,value不可以为nil
        id value;
        //现在假设value 不为nil
        [obj setValue:value forKey:ivarName];
        
        //获取成员变量类型
        NSString *ivarType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"\"" withString:@""];
        ivarType = [ivarType stringByReplacingOccurrencesOfString:@"@" withString:@""];
        //跟成员变量类型 得到对应的类
        Class class = NSClassFromString(ivarType);
        NSLog(@"成员变量的类型是:%@",class);
    }
    return obj;
}
@end

 

 

 

 

 

 

posted @ 2017-05-02 14:32  Gen_0  阅读(326)  评论(0编辑  收藏  举报