美文网首页
runTime常用用法

runTime常用用法

作者: 致在路上的我们 | 来源:发表于2018-02-27 23:43 被阅读0次

动态给分类添加属性

1.创建UIGestureRecognizer的类目 UIGestureRecognizer+Block.h

#import "UIGestureRecognizer+Block.h"
#import <objc/runtime.h>

static const int target_key;

@implementation UIGestureRecognizer (Block)

+ (instancetype) yj_gesterRrecognizerWithAction:(YJBlock)block
{
    __weak typeof(self) weakself = self;
    return [[weakself alloc] initWithActionBlock:block];
}

- (instancetype) initWithActionBlock:(YJBlock)block
{
    self = [self init];
    [self addActionBlock:block];
    [self addTarget:self action:@selector(invoke:)];
    return self;
}

- (void) invoke:(id)sender
{
    YJBlock block = objc_getAssociatedObject(self, &target_key);
    if (block){
        block(sender);
    }
}

- (void) addActionBlock:(YJBlock)block
{
    if (block){
        objc_setAssociatedObject(self, &target_key, block, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
}

 objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)
  • id object 给谁添加就是谁的对象
  • const void *key 关联对象的key
  • id value 被关联者(要添加的属性),这里是一个block()
  • objc_AssociationPolicy policy : 关联时采用的协议,有assign,retain,copy等协议,一般使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
    2.在创建的类中进行调用
UIView *viewM = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    viewM.backgroundColor = [UIColor grayColor];
    [self.view addSubview:viewM];
    [viewM addGestureRecognizer:[UITapGestureRecognizer yj_gesterRrecognizerWithAction:^(id gesterRecognizer) {
        NSLog(@"点击了view");
    }]];

方法的交换

1.创建UIImage的类目UIImage+hook.h

#import "UIImage+hook.h"
#import <objc/runtime.h>
@implementation UIImage (hook)

+(void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class selfClass = object_getClass([self class]);
        SEL oriSEL = @selector(imageNamed:);
        Method oriMethod = class_getInstanceMethod(selfClass, oriSEL);
        
        SEL cusSEL = @selector(myImageNamed:);
        Method cusMethod = class_getInstanceMethod(selfClass, cusSEL);
        
        BOOL addSuccess = class_addMethod(selfClass, oriSEL, method_getImplementation(cusMethod), method_getTypeEncoding(cusMethod));
        if (addSuccess){
            class_replaceMethod(selfClass, cusSEL, method_getImplementation(oriMethod), method_getTypeEncoding(oriMethod));
        }else{
            method_exchangeImplementations(oriMethod, cusMethod);
        }
        
    });
}

+(UIImage *)myImageNamed:(NSString *)name
{
    NSString *newName = [NSString stringWithFormat:@"%@%@",@"new_",name];
    return [self myImageNamed:newName];
}

@end

//替换方法

class_replaceMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)
  • 第一个参数 class: 给哪个类添加参数
  • 第二个参数 SEL : 被替换的那个方法
  • 第三个参数 IMP : A function which is the implementation of the new method. The function must take at least two arguments—self and _cmd.
  • 第四个参数 types :An array of characters that describe the types of the arguments to the method.
    2.调用
UIImageView *subView = [[UIImageView alloc] initWithFrame:CGRectMake(100, 100, 100, 100)];
    //图片的实际名字是 new_Cart
    [subView setImage:[UIImage imageNamed:@"Cart"]];
    [self.view addSubview:subView];

字典转模型

1.创建NSObjec的类别 NSObject+hook.h

#import "NSObject+hook.h"
#import <objc/runtime.h>
@implementation NSObject (hook)

const char *kPeropertyListKey = "kkkkkPeropertyListKey";

+ (instancetype) modelWithdict:(NSDictionary *)dict
{
    /* 实例化对象*/
    id objc = [[self alloc] init];
    /* 使用字典,设置对象信息*/
    /* 1.或者self的属性列表*/
    NSArray *propertyList = [self yj_PropertyList];
    /* 2.遍历字典*/
    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        /* 3.判断 key是否 propertyList中*/
        if ([propertyList containsObject:key]){
            /* 说明属性存在,可以使用kvc 设置数值*/
            [objc setValue:obj forKey:key];
        }
    }];
    /*返回对象*/
    return objc;
}


- (NSArray *) yj_PropertyList
{
    NSArray *ptyList = objc_getAssociatedObject(self, kPeropertyListKey);
    /* 如果 ptyList有值,直接返回*/
    if (ptyList){
        return ptyList;
    }
    /* 调用运行时方法, 取得类的属性列表 */
    /* 成员变量:
     * class_copyIvarList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 方法:
     * class_copyMethodList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 属性:
     * class_copyPropertyList(__unsafe_unretained Class cls, unsigned int *outCount)
     * 协议:
     * class_copyProtocolList(__unsafe_unretained Class cls, unsigned int *outCount)
     */
    unsigned int outCount = 0;
    /**
     * 参数1: 要获取得类
     * 参数2: 类属性的个数指针
     * 返回值: 所有属性的数组, C 语言中,数组的名字,就是指向第一个元素的地址
     */
    /* retain, creat, copy 需要release */
    objc_property_t *properrtyList = class_copyPropertyList([self class], &outCount);
    NSMutableArray *mtArray = [NSMutableArray array];
    /* 遍历所有属性*/
    for (unsigned int i = 0; i < outCount; i++) {
        /* 从数组中取得属性 */
        objc_property_t property = properrtyList[i];
        /* 从 property 中获得属性名称 */
        const char *propertyName_C = property_getName(property);
        /* 将 C 字符串转化成 OC 字符串 */
        NSString *propertyName_OC = [NSString stringWithCString:propertyName_C encoding:NSUTF8StringEncoding];
        [mtArray addObject:propertyName_OC];
    }
    /* 设置关联对象 */
    /**
     *  参数1 : 对象self
     *  参数2 : 动态添加属性的 key
     *  参数3 : 动态添加属性值
     *  参数4 : 对象的引用关系
     */
    objc_setAssociatedObject(self, kPeropertyListKey, mtArray.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    /* 释放 */
    free(properrtyList);
    return mtArray.copy;
}
@end

2.创建模型

#import <Foundation/Foundation.h>
#import "NSObject+hook.h"
@interface Model : NSObject

@property (copy,nonatomic) NSString *name;

@property (copy,nonatomic) NSString *sex;

@property (copy,nonatomic) NSString *age;

@end

3.调用

NSDictionary *dic = @{@"name":@"我爱NBA",
                          @"sex":@"男",
                          @"age":@25
                          };
    Model *model = [Model modelWithdict:dic];
    NSLog(@"name:%@  sex:%@  ",model.name,model.sex);
2018-02-28 09:50:32.343396+0800 RunTime使用场景[18298:1609962] name:我爱NBA  sex:男

对私有属性进行修改

#pragma mark - 获取所有的属性(包括私有的)
- (void) getAllIvar
{
    unsigned int count = 0;
    //Ivar: 定义对象的实例变量,包括类型和名字
    //获取所有的属性(包括私有的)
    Ivar *ivars = class_copyIvarList([NSString class], &count);
    for (int i = 0; i< count; i++) {
        //去除成员变量
        Ivar ivar = ivars[i];
        NSString *name = [NSString stringWithUTF8String:ivar_getName(ivar)];
        NSString *type = [NSString stringWithUTF8String:ivar_getTypeEncoding(ivar)];
        NSLog(@"属性->%@ 和 %@",name,type);
    }
}

- (void) getAllMethod
{
    unsigned int count = 0;
    Method *methodList = class_copyMethodList([UIPageControl class], &count);
    for (int i = 0 ; i< count; i ++) {
        Method method = methodList[i];
        NSString *methodName = NSStringFromSelector(method_getName(method));
        NSLog(@"方法名 ->%@",methodName);
    }
}

归档解档

1.创建YYModel

#import <Foundation/Foundation.h>

@interface YYModel : NSObject <NSCoding>

@property(nonatomic,assign) NSInteger age;
@property(nonatomic,copy) NSString *name1;
@property(nonatomic,copy) NSString *name2;
@property(nonatomic,copy) NSString *name3;
@property(nonatomic,copy) NSString *name4;
@property(nonatomic,copy) NSString *name5;

@end

------------------------------.m文件

#import "YYModel.h"
#import <objc/runtime.h>

@implementation YYModel
//NSKeyedUnarchiver 从二进制流读取对象。
//NSKeyedArchiver 把对象写到二进制流中去。


// 读取实例变量,并把这些数据写到coder中去。序列化数据
//1)、如果是类 就用encodeObject: forKey:
//2)、如果是普通的数据类型就用 eg、encodeInt: forKey:
- (void)encodeWithCoder:(NSCoder *)aCoder
{
    unsigned int count = 0;
    //利用runtime获取实例变量的列表
    Ivar *ivarList = class_copyIvarList([self class], &count);
    for (int i =0; i< count; i++) {
        //读出i位置对应的实例变量
        Ivar ivar =ivarList[i];
        //查看实例变量的名字
        const char *name = ivar_getName(ivar);
        //c语言字符串转化为nsstring
        NSString *namestr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
        //利用kvo取出属性对应的值
        id value = [self valueForKey:namestr];
        //归档
        [aCoder encodeObject:value forKey:namestr];
    }
    //记住c语言copy出来的要进行释放
    free(ivarList);
}

//从coder中读取数据,保存到相应的变量中,即反序列化数据
//1)、如果是类 就用decodeObjectForKey:
//2)、如果是普通的数据类型就用 eg、decodeIntForKey:
- (nullable instancetype)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivarList = class_copyIvarList([self class], &count);
        for (int i = 0 ; i< count; i++) {
            Ivar ivar = ivarList[i];
            const char *name = ivar_getName(ivar);
            NSString *namestr = [NSString stringWithCString:name encoding:NSUTF8StringEncoding];
            id value = [aDecoder decodeObjectForKey:namestr];
            //设置到成员变量身上
            [self setValue:value forKey:namestr];
        }
        free(ivarList);
    }
    return self;
}

@end

2.调用

YYModel *model = [[YYModel alloc] init];
    model.age = 18;
    model.name1 = @"胡航";
    model.name2 = @"梁静茹";
    model.name3 = @"女女生";
    //创建路径
    NSString *docunmentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject];
    NSLog(@"docunmentPath路径:%@",docunmentPath);
    NSString *filePath = [docunmentPath stringByAppendingString:@"/YYModel.data"];
    //存储用户信息,归档
    BOOL result = [NSKeyedArchiver archiveRootObject:model toFile:filePath];
    if (result) {
        NSLog(@"归档成功%@",filePath);
    }else{
        NSLog(@"归档失败");
    }
    
    
    YYModel *yy = [NSKeyedUnarchiver unarchiveObjectWithFile:filePath];
    NSLog(@"年龄%@\nname1:%@\nname2:%@",@(yy.age),yy.name1,yy.name2);

动态添加方法

1.创建类dog

#import "Dog.h"
#import <objc/runtime.h>

@implementation Dog

// 默认方法都有两个隐式参数
void eat(id self,SEL sel){
    NSLog(@"%@ %@",self,NSStringFromSelector(sel));
    NSLog(@"动态添加了一个方法");
}

//当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来
//刚好可以用来判断,未实现的方法是不是我们想要添加动态添加的方法
+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == NSSelectorFromString(@"eat")) {
        //注意:这里需要强转成IMP类型
        class_addMethod(self, sel, (IMP)eat, "v@:");
        return YES;
    }
    //先恢复,不然会覆盖系统的方法
    return [super resolveInstanceMethod:sel];
}
@end

2.实现

Dog *dog = [[Dog alloc] init];
    //默认dog,没有实现eat方法,可以通过performSelector调用,但是会报错
    //动态添加方法就不会报错
    [dog performSelector:@selector(eat)];

另附runtime用法demo

相关文章

  • iOS开发_记录runtime常用的用法

    最近看了下iOS攻城狮DWQ的快速上手Runtime系列文章,记录下runtime的常用用法。 一、消息机制 定义...

  • runTime常用用法

    动态给分类添加属性 1.创建UIGestureRecognizer的类目 UIGestureRecognizer+...

  • runtime 10种常用用法

    分别是通过Objective-C源代码,通过Foundation框架的NSObject类定义的方法,通过对ru...

  • iOS开发之Runtime常用示例总结

    iOS开发之Runtime常用示例总结 iOS开发之Runtime常用示例总结

  • iOS面试点文章链接

    runtime基础方法、用法、消息转发、super: runtime 完整总结 runloop源码、runloop...

  • iOS 开发中 runtime 常用的几种方法

    iOS 开发中 runtime 常用的几种方法 iOS 开发中 runtime 常用的几种方法

  • 基础篇

    Runtime之必备C知识 Runtime之类的本质 Runtime之消息处理策略 Runtime之常用API 进...

  • RunTime用法

    一、什么是RunTime iOS开发过程中,我们一直在与Runtime打交道,可什么是Runtime呢? 对比C语...

  • runtime用法

    在之前学习runtime的过程中,我发现方法交换有两种写法,一开始对对一种写法不太能理解,后来自己写demo来试验...

  • runtime用法

    1.监听网络状态 2.添加分类属性(set / get_assocaite关联) 3.获取类的成员变量以及属性等(...

网友评论

      本文标题:runTime常用用法

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