美文网首页iOS开发知识小集
iOS Runtime学习(三) -- 简单使用篇

iOS Runtime学习(三) -- 简单使用篇

作者: Q海龙 | 来源:发表于2019-03-19 17:15 被阅读19次

一、前言

runtime功能很强大,本文简单的介绍几个实用的小功能,如动态添加属性动态添加方法方法替换字典转模型的几个小例子,本文将对UILabel进行一些简单的操作。
创建工程,创建一个Objective-C File

File:Test
File Type:Category
Class:UILabel

二、添加属性

对UILabel添加一个名为abc的属性

  1. 在UILabel+Test.h文件中,添加如下代码
//添加一个abc属性
@property (nonatomic, strong) NSString *abc;

2.在UILabel+Test.m文件中,添加如下代码

//创建一个关联的key
static NSString *abcStr = @"abcStr";

添加对应的set和get方法

- (void)setAbc:(NSString *)abc {
    objc_setAssociatedObject(self, &abcStr, abc, OBJC_ASSOCIATION_COPY);
}

- (NSString *)abc {
    return objc_getAssociatedObject(self, &abcStr);
}

二、方法替换

我们将新添加的

- (void)setAbc:(NSString *)abc {
    objc_setAssociatedObject(self, &abcStr, abc, OBJC_ASSOCIATION_COPY);
}

替换为

- (void)fu_setAbc:(NSString *)abc {
    NSLog(@"这里是替换setAbc后的方法");
}

我们需要在+(void)load方法中,将这个操作做好

+ (void)load {
    //方法替换 : 将abc的set方法 替换成fu_setAbc
    SEL oldSel = @selector(setAbc:);
    SEL newSel = @selector(fu_setAbc:);
    
    Method oldMethod = class_getInstanceMethod(self, oldSel);
    Method newMethod = class_getInstanceMethod(self, newSel);
    
    //将newSel添加到当前类中,如果当前类有同名的实现,则返回NO
    BOOL boolean = class_addMethod(self, oldSel, method_getImplementation(newMethod), method_getTypeEncoding(newMethod));
    if (boolean) {
        class_replaceMethod(self, newSel, method_getImplementation(oldMethod), method_getTypeEncoding(oldMethod));
    }else {
        method_exchangeImplementations(oldMethod, newMethod);
    }
}

三、动态添加方法

在这里需要使用到performSelector,它是在iOS中的一种方法调用方式。他可以向一个对象传递任何消息,而不需要在编译的时候声明这些方法。
我们就用此方法来调用一下UILabeltest方法

[label performSelector:@selector(test:) withObject:@"test"];

在UILabel+Test.m文件中,添加对应的test方法

void test (id self, SEL _cmd, NSString *str) {
    NSLog(@"动态添加方法成功");
}

然后,我们在resolveInstanceMethod中来实现

+ (BOOL)resolveInstanceMethod:(SEL)sel
{
    if (sel == NSSelectorFromString(@"test:")) {
        //void用v来表示,id参数用@来表示,SEL用:来表示
        class_addMethod(self, sel, (IMP)test, "v@:@");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

在这里要看一下,千万别用错了方法

// 判断对象方法有没有实现
+(BOOL)resolveInstanceMethod:(SEL)sel

// 判断类方法有没有实现
+ (BOOL)resolveClassMethod:(SEL)sel

四、字典转模型

创建一个类DataItem如下所示

@interface DataItem : NSObject
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *tel;
@property (nonatomic, strong) NSArray *list;
+ (NSDictionary *)arrayPropertyGenericClass;
+ (DataItem *)toItem:(NSDictionary *)dict;
@end
#import "DataItem.h"
#import <objc/runtime.h>
#import <objc/message.h>

@implementation DataItem

+ (NSDictionary *)arrayPropertyGenericClass
{
    return @{
             @"list":[DataItem class]
             };
}

+ (id)toItem:(NSDictionary *)dict {
    
    Class class = objc_msgSend(objc_msgSend([self class], @selector(alloc)), @selector(init));
    
    NSDictionary *classDict = [DataItem arrayPropertyGenericClass];
    
    //成员变量个数
    unsigned int outCount = 0;
    
    //获取DataItem所有成员变量的数组
    Ivar *ivarList = class_copyIvarList(self, &outCount);
    
    for (int i = 0; i < outCount; i++) {
        //将成员变量从list中取出
        Ivar oneIvar = ivarList[i];
        
        //转换成NSString, 转换后的成员变量名字前面都带有 “_”
        NSString *oneIvarStr = [NSString stringWithUTF8String:ivar_getName(oneIvar)];
        
        //去掉 _
        oneIvarStr = [oneIvarStr substringFromIndex:1];
        
        //从字典中取出对应的value
        id value = dict[oneIvarStr];
        
        if ([classDict.allKeys containsObject:oneIvarStr]) {
            //这个是array类型
            Class subClass = classDict[oneIvarStr];
            NSArray *valueArray = (NSArray *)value;
            __block NSMutableArray *newMutList = [NSMutableArray arrayWithCapacity:outCount];
            [valueArray enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                NSDictionary *oneDict = (NSDictionary *)obj;
                id newClass = [subClass toItem:oneDict];
                [newMutList addObject:newClass];
            }];
            value = newMutList;
        }
        
        if (value != nil) {
            [class setValue:value forKey:oneIvarStr];
        }
    }
    
    return class;
}

@end

字段转模型的主要思路就是

  • 将类的所有成员变量通过class_copyIvarList提取出来
  • 将这些变量转换成对应的String对象
  • 在字典中找出所对应的value
  • 利用KVC将value存入对应的key当中

如果是array类型,就需要指定一下里面的元素所对应的类,然后重复上面的操作即可。

五、总结

古有学好数理化,走遍全天下!
今有学会Runtime,干啥都不怕!
上面例子的下载地址
有什么想要用到runtime实现的小功能,欢迎在评论留言。如果有时间,我会试着做一下。

相关文章

网友评论

    本文标题:iOS Runtime学习(三) -- 简单使用篇

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