Runtime 的一些用法

来源:互联网 发布:金蝶软件数据导出 编辑:程序博客网 时间:2024/05/01 07:18

runtime 就是OC 中经常说的 运行时


这里 简单介绍一下 OC 中用到一些场景

1、字典转模型  

2、给分类添加关联对象

3、交换方法


runtime  使用的时候一般建立一个 NSObject 的分类Cotegory 。 当然也可以根据 实际情况创建其他类的Cotegory。在创建的文件里面需要导入

#import <objc/runtime.h>

、字典转模型。

首先 动态的获取 类 的属性名称 -> 然后使用 KVC 进行赋值.

1、首先获取类的属性名称

.h 

/**    获取 属性的名称数组    @return  返回对象的属性名称数组 */+ (NSArray *)cz_objPropertiesAry;
.m

const void *kPropertiesKey = "kPropertiesKey";+ (NSArray *)cz_objPropertiesAry{    //获取属性数组的指针数组    unsigned int count = 0;    objc_property_t *property = class_copyPropertyList([self class], &count);        //创建存放数据的数组    NSMutableArray *array = [NSMutableArray array];        //遍历属性数组    for (unsigned int i = 0; i < count; i++) {                // 指针   C语言中数组的名字 为第一个元素的指针        objc_property_t pty = property[i];                //获取属性的名字  类型为C的字符串        const char *cName  =  property_getName(pty);                //把C字符串转化为OC字符串        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];                //[NSString stringWithUTF8String:cName]; 也可以                //把信息添加到数组里面        [array addObject:name];            }        //释放属性数组    free(property);        return array.copy;}

unsigned int count =0//下面为数组

 objc_property_t *proList = class_copyPropertyList([selfclass], &count);

注意,这个方法获取的数组最后要用  free(proList);  释放掉


class_copyPropertyList 获取 类的属性的列表

class_copyIvarList   获取类的 成员变量的列表

class_copyMethodList 获取类 的方法的列表

class_copyProtocolList  获取类的代理的列表

参数 

1、 要获取的这个类 因为是Cotegory 分类 ,使用 self

2、列表里面元素的个数      unsigned int  无符号的整形

返回值:

所有属性的数组      类型为 objc_property_t 的数组


再遍历类的属性数组 ,因为类型为   objc_property_t  是一个指针,使用property_getName获取这个指针对应属性的名字。

使用property_getName 获取的是一个const char  类型C 语言的字符串  再转化为OC的字符串 。最后添加到存储数据的数组里面。


获取到类的属性名称后,就可以使用KVC 就行赋值了。

//所有字典转模型框架 核心算法+ (instancetype)cz_objcWithDictionary:(NSDictionary *)dict{    //实例化对象    id object = [[self alloc]init];        //获取对象的属性名称数组    //1> 获得 self 的属性列表    NSArray *array = [self cz_objPropertiesAry];        //遍历字典  使用KVC 为数组中的属性赋值    [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {                //需要先判断数组中是否包含 字典的Key        if ([array containsObject:key]) {                        //赋值            [object setValue:obj forKey:key];        }    }];        return object;}


使用:

创建一个Person 类  不实现.m

#import <Foundation/Foundation.h>@interface Person : NSObject@property (nonatomic, copy) NSString *name;@property (nonatomic, assign) NSInteger age;@property (nonatomic, assign) double height;@property (nonatomic, copy) NSString *title;@end

在ViewController 里面

导入  

 #import "Person.h"     

#import "NSObject+Runtime.h"

  //获取Persion的属性列表数组    NSArray *properties =   [Person cz_objPropertiesAry];    NSLog(@"%@",properties);        NSDictionary *dic = @{@"name":@"张三",@"age":@(22),@"height":@(168),@"title":@"运行时",@"place":@"boss"};    Person *persion = [Person cz_objcWithDictionary:dic];      NSLog(@"%@",persion);


如果因为iOS 在运行的时候 里面的类的对象不会改变,重复的获取 类 的属性名称 会造成 影响APP性能,这个时候可以使用关联对象的方法。


二、添加关联 动态的添加属性值

修改获取类属性的方法

const void *kPropertiesKey = "kPropertiesKey";+ (NSArray *)cz_objPropertiesAry{    #pragma mark 关联对象 获取 属性值 没有 则添加属性值  (动态的添加属性值)    /*     此方法虽然能够获取类的属性数组  但是如果每次调用都要执行一次的话 耗费的时间长,     使用 关联对象  动态的添加属性值  分别在方法的开头和结尾          */    /*     参数:        1、对象  self         2、const void 的key          返回值  : Id类型     添加的属性值     */        NSArray *proList =  objc_getAssociatedObject(self, kPropertiesKey);    if (proList) { //如果获取的关联对象里面有元素 就直接返回        return proList;    }        //获取属性数组的指针数组    unsigned int count = 0;    objc_property_t *property = class_copyPropertyList([self class], &count);        //创建存放数据的数组    NSMutableArray *array = [NSMutableArray array];        //遍历属性数组    for (unsigned int i = 0; i < count; i++) {                // 指针   C语言中数组的名字 为第一个元素的指针        objc_property_t pty = property[i];                //获取属性的名字  类型为C的字符串        const char *cName  =  property_getName(pty);                //把C字符串转化为OC字符串        NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];                //[NSString stringWithUTF8String:cName]; 也可以                //把信息添加到数组里面        [array addObject:name];            }        //释放属性数组    free(property);    #pragma mark 关联对象 2 添加属性值 动态的添加属性值        /*     参数:        1、对象 self        2、const void 的key  同第一步        3、添加的属性值          4、关联的协议     */    objc_setAssociatedObject(self, kPropertiesKey, array.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);        return array.copy;}

修改的方法 里面 多了几行代码

 NSArray *proList =  objc_getAssociatedObject(self, kPropertiesKey);    if (proList) {        return proList;    }


objc_setAssociatedObject(self, kPropertiesKey, array.copy, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

分别在方法的开始和结尾

objc_getAssociatedObject    获取关联对象动态添加的属性值

参数:

1、对象 self

2、const void 的key 

返回值:

Id类型, 添加的属性值


objc_setAssociatedObject 设置动态添加关联对象的属性值

参数:

1、对象 self 

2、const void 的key 跟上面获取的key一致

3、要添加的属性值,(如果设置了,上面的objc_getAssociatedObject 就能直接获取到,下次调用就能直接获取属性值)

4、关联的协议   OBJC_ASSOCIATION_RETAIN_NONATOMIC


添加了 了关联对象 的方法,下次调用该方法的时候,运行到objc_getAssociatedObject 就能直接获取到 数据了,不用再往下执行了。


三、交换方法

特点: 

在无法修改系统方法 ,和第三方框架的时候,

1、利用交换方法,先执行自定义的方法

2、再执行系统方法或第三方框架方法。

被称之为 黑魔法 ,对系统和框架有很强的依赖性。


举个例子:

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 200, 200)];    imageView.center = self.view.center;    [self.view addSubview:imageView];        imageView.image = [UIImage imageNamed:@"image5.png"];

在UIImageView 的时候,系统的设置图片的方法, imageView.image = [UIImage imageNamed:@""]; 

imageView.image  相当于 imageView setImage: UIImage

我们可以自定义一个方法,替换掉系统的setImage: 方法

创建一个UIImageView 的Cotegory

//在类 被加载到运行时的时候,就会执行+ (void)load{        //交叉方法  就下面的3句话        //获取 类 实例化的原始方法   setImage:    Method originalMethod = class_getInstanceMethod([self class], @selector(setImage:));        //获取 自定义的类的实例化的方法  cz_setImage:    Method swizzleMethod = class_getInstanceMethod([self class], @selector(cz_setImage:));        //交换两个方法 setImage: 和 cz_setImage:  完成之后    //1> 调用setImage: 相当于调用 cz_setImage:    //2> 调用 cz_setImage: 相当于调用 setImage:    method_exchangeImplementations(originalMethod, swizzleMethod);}//自定义 的 类 的实例化方法  Cotegory- (void)cz_setImage:(UIImage *)image{    //这里是我们想要做的事情    NSLog(@"调用的自定义的方法: %s",__FUNCTION__);     <span style="white-space:pre"></span>    //再调用系统 的默认方法    /*重点 :            为什么 方法名是自定义方法 而不是系统默认的方法名字            是因为load 里面 系统默认方法和自定义的方法进行了交换              系统默认的方法 setImage: 变成了 cz_setImage:             所以 在交换方法完成之后 再次调用系统默认的方法就变成了 我们自定义的方法          */    [self cz_setImage:result];}

首先先自定义一个设置图片的方法- (void) cz_setImage:(UIImage *)image 

里面 输出 当前的方法。


在方法 + (void) load{} 里面添加 我们的交换方法

就3句话,

1、获取系统默认的 设置图片的方法

2、获取自定义的设置图片的方法

3、交换这两个方法

class_getInstanceMethod  获取类的实例的方法

参数:

1、类的实例  [self class]   (这是一种特殊的实例化对象)

2、获取的实例方法  SEL 

返回值:

Method  方法 类型


method_exchangeImplementations

参数:

1、要交换的第一个方法

2、要交换的第二个方法


重点注意:

在方法交换之后,两个方法已经被交换了,调用系统方法 变成调用自定义的方法名

调用自定义的方法 变成调用系统的方法名


所以下面:

//自定义 的 类 的实例化方法  Cotegory- (void)cz_setImage:(UIImage *)image{    //这里是我们想要做的事情  输出当前调用的方法    NSLog(@"调用的自定义的方法: %s",__FUNCTION__);     <span></span>    //再调用系统 的默认方法    /*重点 :            为什么 方法名是自定义方法 而不是系统默认的方法名字            是因为load 里面 系统默认方法和自定义的方法进行了交换              系统默认的方法 setImage: 变成了 cz_setImage:             所以 在交换方法完成之后 再次调用系统默认的方法就变成了 我们自定义的方法          */  这个时候系统默认的方法名 在这里变成了   <span style="font-family: Arial, Helvetica, sans-serif;">cz_setImage:</span>    [self cz_setImage:result];}

ViewController 的运行结果为

#import "UIImageView+RuntimeCrossoverMethod.h"

UIImageView *imageView = [[UIImageView alloc]initWithFrame:CGRectMake(0, 0, 200, 200)];    imageView.center = self.view.center;    [self.view addSubview:imageView];        imageView.image = [UIImage imageNamed:@"image5.png"];

2016-10-14 16:38:01.248 Runtime[4947:837294]调用的自定义的方法: -[UIImageView(RuntimeCrossoverMethod) cz_setImage:]


我们调用系统默认的方法 变成调用我们自定义的方法,当然我们在自定义方法里面又掉用了系统的方法(这个时候系统的方法名变成了我们自定义的方法名)。



0 0
原创粉丝点击