美文网首页
iOS Runtime应用示例

iOS Runtime应用示例

作者: 一片姜汁 | 来源:发表于2018-06-08 17:18 被阅读3次

    一.使用objc_msgSend()发消息

    使用objc_msgSend()没有参数提示且不可以传入参数时需要进行下列设置:
    TARGETS->BuildSetting 搜索msg->Enable Strict Checking of objc_msgSend Calls 设置为NO

    • 新建Person类
    Person.h
    #import <Foundation/Foundation.h>
    @interface Person : NSObject
    + (void)eat;
    - (void)eat;
    - (void)run:(int)meter;
    @end
    Person.m
    #import "Person.h"
    @implementation Person
    + (void)eat {
        NSLog(@"类方法-吃东西");
    }
    - (void)eat {
        NSLog(@"对象方法-吃东西");
    }
    - (void)run:(int)meter {
        NSLog(@"跑了%d米",meter);
    }
    @end
    
    • 发送消息 调用对象方法:
    一般调用对象方法的方式:
        Person *p = [[Person alloc] init];
        [p eat];
        [p performSelector:@selector(eat)];
    使用objc_msgSend发消息:
        Person *p = [[Person alloc] init];    
        /*
         objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
         self:消息的接收方
         op :方法选择器
         ... : 更多参数
         */
        objc_msgSend(p, @selector(eat));//无参
        objc_msgSend(p, @selector(run:),1000);//有参
    
    • 发送消息 调用类方法:
    一般调用类方法的方式:
        [Person eat];
        [[Person class] performSelector:@selector(eat)];
    使用objc_msgSend发消息:
        objc_msgSend([Person class], @selector(eat));
    

    二.使用method_exchangeImplementations()交换方法

    • 应用场景
      拦截系统方法 指定其的具体实现为自定义的方法
    • 实例
      拦截UIImage的imageNamed:方法 让其判断图片是否存在
    给UIImage新建一个Category
    分类.h文件
    #import <UIKit/UIKit.h>
    @interface UIImage (UIImage)
    + (__kindof UIImage *)jsh_imageNamed:(NSString *)imageName;
    @end
    分类.m文件
    #import "UIImage+UIImage.h"
    #import <objc/message.h>
    @implementation UIImage (UIImage)
    +  (void)load {
        //获取需要交换的方法
        Method imageNamedMethod = class_getClassMethod([UIImage class], @selector(imageNamed:));
        Method jsh_imageNamedMethod = class_getClassMethod([UIImage class], @selector(jsh_imageNamed:));
        /*
         交换方法实现
         交换以后:
         调用imageNamedMethod 就是调用 jsh_imageNamedMethod
         调用jsh_imageNamedMethod 就是调用imageNamedMethod
         */
        method_exchangeImplementations(imageNamedMethod, jsh_imageNamedMethod);
    }
    + (UIImage *)jsh_imageNamed:(NSString *)imageName {
        //1.加载图片
        UIImage *image = [UIImage jsh_imageNamed:imageName];//此处本质是调用imageNamed
        //2.判断图片是否存在
        if (!image) {
            NSLog(@"%@图片不存在",imageName);
        }
        return image;
    }
    @end
    
    运行下列代码可以判断图片名为123的图片是否存在:
    UIImage *image = [UIImage imageNamed:@"123"];
    

    三.使用class_addMethod()动态添加方法

    新建一个Animation类:

    Animation.h
    #import <Foundation/Foundation.h>
    @interface Animation : NSObject
    @end
    Animation.m
    #import "Animation.h"
    @implementation Animation
    @end
    

    运行下列代码:

    Animation *animation = [[Animation alloc] init];
    [animation performSelector:@selector(eat)];
    

    控制台报错:

    '-[Animation eat]: unrecognized selector sent to instance
    
    • 解决方案
      更改Animation.m文件
    #import "Animation.h"
    #import <objc/message.h>
    /*
     方法类型
     void(id,SEL)
     id SEL 是隐含参数
     */
    //此处方法名可以随意定义
    void eat(id self,SEL _cmd) {
        NSLog(@"动物在吃东西");
    };
    @implementation Animation
    /*
     当我们调用了一个没有实现的方法时,系统就会调用这个方法
     如果我们调用的方法有实现,系统是不会调用这个方法的
     */
    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == @selector(eat)) {
            /*
             class_addMethod()参数介绍:
             cls:给哪个类添加方法
             name:添加方法的方法编号
             imp:方法实现/函数入口/函数名
             types:方法类型/函数类型
             */
            /*
             types介绍:
             v 代表void:函数bbb()的返回值是空
             @ 代表对象:函数bbb()的第一个参数是id类型
             : 代表SEL:函数bbb()的第二个参数是SEL
             */
            class_addMethod(self,sel, (IMP)eat, "v@:");
            return YES;
        } else {
            return [super resolveInstanceMethod:sel];
        }
    }
    @end
    此时便可正常运行下列代码:
        Animation *animation = [[Animation alloc] init];
        [animation performSelector:@selector(eat)];
    

    四.使用objc_setAssociatedObject()和objc_getAssociatedObject()为分类添加属性

    • 方法介绍
    /**
    设置关联对象
    @param object 需要关联的对象
    @param key 需要关联的属性名称
    @param value 需要关联的属性的值
    @param policy 关联策略
     */
    objc_setAssociatedObject(id  _Nonnull object, const void * _Nonnull key, id  _Nullable value, objc_AssociationPolicy policy)
    
    /**
    获取关联对象
    @param object 关联的对象
    @param key 关联的属性名称
     */
    objc_getAssociatedObject(id  _Nonnull object, const void * _Nonnull key)
    
    • 示例:为NSObject 添加一个含有属性的分类ShowTime
    NSObject+ShowTime.h
    #import <Foundation/Foundation.h>
    @interface NSObject (ShowTime)
    @property (nonatomic,strong) NSDate *currentTime;
    @end
    
    NSObject+ShowTime.m
    #import "NSObject+ShowTime.h"
    #import <objc/message.h>
    @implementation NSObject (ShowTime)
    - (void)setCurrentTime:(NSDate *)currentTime {
        //设置关联对象 添加属性
        /*
         参数说明:
         object:需要关联的对象
         key:需要关联的属性的名称
         value:需要关联的属性的值
         policy:关联策略
         */
        objc_setAssociatedObject(self, @"currentTime", currentTime, OBJC_ASSOCIATION_RETAIN);
    }
    - (NSDate *)currentTime {
        //获取关联对象 获取属性的值
        return objc_getAssociatedObject(self, @"currentTime");
    }
    @end
    
    • 关联以后可正常运行下列代码
        NSObject *objc = [[NSObject alloc] init];
        objc.currentTime = [NSDate date];
        NSLog(@"%@",objc.currentTime);
    

    相关文章

      网友评论

          本文标题:iOS Runtime应用示例

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