runTime常用用法

动态给分类添加属性

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,636评论 0 9
  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,108评论 0 10
  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    made_China阅读 1,186评论 0 6
  • runtime 和 runloop 作为一个程序员进阶是必须的,也是非常重要的, 在面试过程中是经常会被问到的, ...
    SOI阅读 21,605评论 3 63
  • 今天我为什么会难过呢 我以为我对别人好别人也会对我好呢,事实上人家根本就不在乎你。 我一直在心里问自己,如果那天换...
    小腾腾阅读 207评论 0 0