美文网首页iOS Developer将来跳槽用
ios中Runtime的介绍以及使用

ios中Runtime的介绍以及使用

作者: Hither | 来源:发表于2016-08-28 02:23 被阅读251次

    ios黑魔法--runtime介绍:

    在Xcode5以后 ,苹果不建议开发者使用底层。为了能够使用runtime,我们需要做下面两个配置:

    1.在需要使用runtime的地方导入#import<objc/message.h>。
    2.在工程的Build Setting中搜索msg ,将其修改为NO。

    引入:

    在开发中,用对象去调用方法:

    如:我们定义了一个Person类
    其中有两个方法:

    -(void)eat;//对象方法
    +(void)eat;//类方法
    
    Person *p = [[Person alloc]init];
    [p eat];//调用对象方法
    //p这个对象调用eat方法实际上是发送了一个消息去找到eat这个方法,并执行了这个方法:
    objc_msgSend(p,@Selector(eat));
    
    //类调用类方法,实际上也是用类对象去调用方法
    Class personName = [Person class];
    [personName PerformSelector:@Selector(eat)];
    //实际上也执行了这个方法:
    objc_msgSend(personName,@Selector(eat));
    //PerformSelector这个方法就是一种动态添加方法,动态添加方法是一种懒加载的机制。
    

    利用runtime扩展系统功能

    如:现在有这样一个需求,imageName加载图片,但是我们并不能知道图片加载成功与否,我们想添加一个方法,知道image是否添加成功,所以我们就需要使用runtime来改造。

    1.新建一个UIImage的category
    2.导入#import<objc/message.h>
    3.提供一个自己的方法:

    +(_kindof UIImage*)hjt_imageNamed:(NSString *)imageName
    {
        //加载图片
          UIImage *image = [UIImage imageNamed:imageName];
        //进行图片是否为空的判断
        if(image==nil){
        NSLog(@"加载的图片为空,请注意");
      }
    return image;
    }
    

    4.重写load方法

    +(void)load{
      //Class_getInstanceMethod:获取对象方法
      //Class_getMethodImplementation:获取类方法的实现
      //Class_getClassMethod:获取类方法
    Method imageNameMethod =  Class_getClassMethod([UIImage class],@Selector(imageNamed:));
    Method hjt_imageNameMethod =  Class_getClassMethod([UIImage class],@Selector(hjt_imageNamed:));
    //交换方法实现对系统方法的扩展
    method_exchangeImplementations(imageNameMethod ,hjt_imageNameMethod);
    //外部调用的时候还是调用imageNamed:
    }
    

    使用runtime动态添加方法:

    • 对于动态添加方法实际上就是需要借助PerformSelector:这个方法来做事

    如:我们为Person这个类动态添加一个eat的方法;

    Person.m
    //1.动态添加方法的第一步,先实现resolveInstanceMethod
    //当我们在外面调用一个没有实现的方法时,就会调用resolveInstanceMethod
    //备注:SEl其实只是一个方法的编号,系统会根据这个编号去找这个方法;
    +(BOOL)resolveInstanceMethod:(SEL)sel{
    if(sel==@Selector(eat)){
    //"V@:"这个需要查官方runtime的文档,v表示eatMethod的返回值void
    //@表示objc,:表示Selector
    Class_addMethod(self,sel,(IMP)eatMethod,"v@:");
    return YES;
    }
    return [super resolveInstanceMethod:sel];
    }
    
    // 定义eatMethod函数  注意是函数!
    void eatMethod(id self,SEL _cmd){
    
    }
    
    //来到外部 我们初始化Person 然后调用eat这个方法
    Person *p = [[Person alloc]init];
    //这个是调用的不带参数的eat方法
    [p performSelector:@Selector(eat)];
    //如果要调用带参数的eat方法,我们需要进行下面几个地方的修改
    //带参数:[p performSelector:@Selector(eat) withObject:@"apple"];
    //1.修改Person.m中resolveInstanceMethod方法里的sel==@Selector(eat:);
    //2.定义的函数eatMethod的时候,新增加一个参数 id param
    //3.修改Class_addMethod方法中最后一个参数为"v@:@";
    

    使用runtime动态添加属性

    • 如:我们为NSObject添加一个userName这个属性
      //动态添加属性就是一种动态的关联,让对象的某个属性去关联某块内存
    1.新建一个NSObject的category
    2.给某个对象产生关联,添加属性
    //object:给那个对象添加属性  key:属性的名称(通过这个key拿到关联的对象) value:关联的值 
    -(void)setName:(NSString*)name{
    objc_setAssociatedObject(self,@"userName",userName,OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
      -(NSString*)name{
      return objc_getAssociatedObject(self,@"userName");
      }
    //这样我们就可以拿到NSObject对象中的userName这个属性了
    

    使用runtime 进行字典转模型

    1.写一个NSObject的category
    在NSObject+model.m中:
    +(instancetype)modelWithDict:(NSDictionary*)dict{
    id obic = [[self alloc] init];
    //class_copyIvarList:将成员属性列表复制一份传出去
    //Ivar*:指向一个ivar数组的指针 count:成员属性的个数
    unsigned int count = 0;
    Ivar *ivarList = class_copyIvarList(self,&count);
    for (int i  = 0; i < count; i++){
    //获取成员属性
      Ivar ivar = ivarList [i];
    //获取成员名
    NSString *propertyName = [NSString stringWithUTF8String:ivar_getName(ivar)];
    //获取数据类型
    NSString *propertyType = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
    //获取key substringFromIndex:1 去除第0个_  即将_count这种转换成count
    NSString* key = [propertyName substringFromIndex:1];
    id value = dict[key];
    //对于解析出来还是一个字典,如 其中有一个字典 但是我们已经新建了一个User去解析这个字典,只有遇到这个User我们才进行这种二级转换
    //二级转换
    //值是字典,成员属性的类型不是字典,才需要转换成模型
    //下面这个判断的意思就是遇到我们自定义的User*user进行转换  而NSDiction*dic不转换
    if([value iskindofClass:[NSDictionary class]]&&![propertyType containString:@"NS"]){
    NSRange range = [propertyType rangeOfString:@"\""];
    propertyType = [propertyType  substringFromIndex:range.location+range.length];
    range = [propertyType rangeOfString:@"\""];
    propertyType = [propertyType  substringToIndex:range.location];
    class modelClass = NSClassFromString(property);
    if(modelClass){
      value = [modelClass modelWithDict:value];
    }
    }
    if(value){
    [objc setValue:value forkey:key];
    }
    
    }
    return objc;
    }
    
    
    //为UIbutton添加block 方便使用
    //UIButton+HJTBlock.h
    -(void)hjt_addEventHandler:(void (^) (UIButton *sender)) block forUIControlEvents:(UIControlEvents)controlEvents;
    
    
    //UIButton+HJTBlock.m
    #import "UIButton+HJTBlock.h"
    #import <objc/runtime.h>
    
    typedef void(^HJT_ButtonEventsBlock)(UIButton *sender);
    
    @interface UIButton ()
    
    //event callback
    @property (nonatomic,copy) HJT_ButtonEventsBlock hjt_buttonEventBlock;
    
    @end
    
    @implementation UIButton (HJTBlock)
    
    static void *hjt_buttonEventsBlockKey = &hjt_buttonEventsBlockKey;
    
    -(HJT_ButtonEventsBlock)hjt_buttonEventBlock{
        return objc_getAssociatedObject(self,&hjt_buttonEventsBlockKey);
    }
    
    -(void)setHjt_buttonEventBlock:(HJT_ButtonEventsBlock)hjt_buttonEventBlock{
        objc_setAssociatedObject(self, &hjt_buttonEventsBlockKey,hjt_buttonEventBlock,OBJC_ASSOCIATION_COPY);
    }
    
    -(void)hjt_addEventHandler:(void (^)(UIButton *))block forUIControlEvents:(UIControlEvents)controlEvents{
        self.hjt_buttonEventBlock = block;
        [self addTarget:self action:@selector(hjt_buttonClick) forControlEvents:controlEvents];
    }
    
    -(void)hjt_buttonClick{
        if (self.hjt_buttonEventBlock) {
            self.hjt_buttonEventBlock(self);
        }
    }
    
    @end
    
    
    //使用:
    UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(20, 20, 40, 40)];
        btn.backgroundColor = [UIColor redColor];
        [btn setTitle:@"hello" forState:UIControlStateNormal];
        [self.view addSubview:btn];
        
         [btn hjt_addEventHandler:^(UIButton *sender) {
             NSLog(@"%@",sender.titleLabel.text);
             sender.backgroundColor = [UIColor blueColor];
         } forUIControlEvents:UIControlEventTouchUpInside];
    

    相关文章

      网友评论

        本文标题:ios中Runtime的介绍以及使用

        本文链接:https://www.haomeiwen.com/subject/wnttettx.html