前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >iOS进阶之runtime作用

iOS进阶之runtime作用

作者头像
Dwyane
发布2018-05-22 17:48:48
5970
发布2018-05-22 17:48:48
举报
文章被收录于专栏:技术总结技术总结

前言

Runtime基本是用C和汇编写的,可见苹果为了动态系统的高效而作出的努力。你可以在这里下到苹果维护的开源代码。苹果和GNU各自维护一个开源的runtime版本,这两个版本之间都在努力的保持一致。Objective-C 从三种不同的层级上与 Runtime 系统进行交互,分别是通过 Objective-C 源代码通过 Foundation 框架的NSObject类定义的方法通过对 runtime 函数的直接调用大部分情况下你就只管写你的Objc代码就行,runtime 系统自动在幕后辛勤劳作着。

1、概念

RunTime简称运行时,就是系统在运行的时候的一些机制,其中最主要的是消息机制。

?、ios进阶之传递消息

?

2、作用

1.动态交换两个方法的实现 2.为类别添加属性(我们知道类别是不能扩展属性的,只能扩展方法,但可以运行时实现,通过为类增加属性) 3.获取某个类的所有成员变量和成员方法 4.实现NSCoding的自动归档和自动解档 5.动态添加对象的成本变量和成员方法 作用:当硬件内存过小的时候,如果我们将每个方法都直接加到内存当中去,但是几百年不用一次,这样就造成了浪费,所有采取动态添加 6.实现字典和模型的自动转换

3、详解:

一、动态交换方法

1.在自定义类DWExchangeTwoMethod.m中

代码语言:javascript
复制
- (instancetype)init {
    if (self = [super init]) {
        Method workMethod = class_getInstanceMethod([DWExchangeTwoMethod class], @selector(work));
        Method playMethod = class_getClassMethod([DWExchangeTwoMethod class], @selector(play));
        method_exchangeImplementations(workMethod, playMethod);
    }
    return self;
}

- (void)play {
    NSLog(@"玩...");
}

+ (void)work {
    NSLog(@"工作...");
}

抛砖引玉:可以交换方法,防止数组越界导致的崩溃

2、然后在其他类调用

代码语言:javascript
复制
    DWExchangeTwoMethod *exchangeMethod = [DWExchangeTwoMethod new];
    [exchangeMethod play];

由上可以看出,调用的是play方法

但打印结果为 【玩...】,证明已经动态交换成功

二、为分类添加属性

一般情况下,分类不可以添加属性,但用runtime却可以实现

代码语言:javascript
复制
- (NSString *)height {
    return objc_getAssociatedObject(self, @"height");
}

- (void)setHeight:(NSString *)height {
    objc_setAssociatedObject(self, @"height", height, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

注:

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy)

id object :表示关联者,是一个对象,变量名理所当然也是object

const void *key :获取被关联者的索引key

id value :被关联者,这里是一个block

objc_AssociationPolicy policy : 关联时采用的协议,有assign,retain,copy等协议,一般使用OBJC_ASSOCIATION_RETAIN_NONATOMIC

objc_getAssociatedObject(id object, const void *key)

参数解析同上

三、获取实例变量列表
代码语言:javascript
复制
    unsigned int outCount = 0;
    Ivar *ivars = class_copyIvarList([DWExchangeTwoMethod class], &outCount);
    for (int i = 0; i < outCount; i++) {
        Ivar ivar = ivars[i];
        const char* name = ivar_getName(ivar);
        const char* type = ivar_getTypeEncoding(ivar);        
        NSLog(@"变量名%s :%s", name, type);
    }
//    此时要注意, 尽管在ARC模式下, 取出变量后要依然手动释放内存, 利用free()方法即可:
    free(ivars);

注:

class_copyPropertyList与class_copyIvarList

class_copyPropertyList返回的仅仅是对象类的属性(@property申明的属性),而class_copyIvarList返回类的所有属性和变量(包括在@interface大括号中声明的变量)

四、实现NSCoding的自动归档和自动解档;

(不用对每个属性edcode和decode了,如果几十个属性一个个的encode和decode真的很麻烦啊,使用运行时可以遍历出每个对象的属性,数组的方式遍历eccode,decode)

代码语言:javascript
复制
#import "DWPerson.h"

#define kname           @"name"
#define kidCard         @"idCard"
#define kage            @"age"
#define kothers         @"others"

@implementation DWPerson

- (void)encodeWithCoder:(NSCoder *)aCoder {
    [aCoder encodeObject:_name forKey:kname];
    [aCoder encodeObject:_idCard forKey:kidCard];
    [aCoder encodeInt:_age forKey:kage];
    [aCoder encodeObject:_others forKey:kothers];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        _name = [aDecoder decodeObjectForKey:kname];
        _age = [aDecoder decodeIntForKey:kage];
        _idCard = [aDecoder decodeObjectForKey:kidCard];
        _others = [aDecoder decodeObjectForKey:kothers];
    }
    return self;
}

@end

上面每个类都要写一次,所以我们添加个分类会更加简化

代码语言:javascript
复制
#import "NSObject+ArchiveExtension.h"
#import <objc/runtime.h>

@implementation NSObject (ArchiveExtension)

// 先对当前类进行编码,然后对父类进行编码,如果父类是NSObject就结束编码
- (void)encode:(NSCoder *)aCoder {
    // 一层层父类往上查找,对父类的属性执行归解档方法
    Class c = self.class;
    while (c && c != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivars = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            id value = [self valueForKey:key];
            [aCoder encodeObject:value forKey:key];
        }
        
        free(ivars);
        c = [c superclass];
    }
}

- (void)decode:(NSCoder *)aDecoder {
    Class c = self.class;
    while (c && c != [NSObject class]) {
        unsigned int outCount = 0;
        Ivar *ivars  = class_copyIvarList(c, &outCount);
        for (int i = 0; i < outCount; i++) {
            Ivar ivar = ivars[i];
            NSString *key = [NSString stringWithUTF8String:ivar_getName(ivar)];
            //注意与归档的顺序不同
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
        free(ivars);
        c = [c superclass];
    }
}

@end

这时,可以在DWPerson.m中取代之前的代码

代码语言:javascript
复制
- (void)encodeWithCoder:(NSCoder *)aCoder {
    [self encode:aCoder];
}

- (instancetype)initWithCoder:(NSCoder *)aDecoder {
    if (self = [super init]) {
        [self decode:aDecoder];
    }
    return self;
}

当然还可以更加简洁,把NSCoding的定义成宏

代码语言:javascript
复制
#ifndef Archive_h
#define Archive_h

#import "NSObject+ArchiveExtension.h"


#define ArchiveImplemention \
\
- (instancetype)initWithCoder:(NSCoder *)aDecoder {\
    if (self = [super init]) {\
        [self decode:aDecoder];\
    }\
    return self;\
}\
\
- (void)encodeWithCoder:(NSCoder *)aCoder {\
    [self encode:aCoder];\
}  //最后这句不用斜线
#endif /* Archive_h */

注:

如果有空行,需要加斜线补充

使用:

代码语言:javascript
复制
//宏定义
#import "Archive.h"

@implementation DWPerson

ArchiveImplemention

@end
五、动态添加方法

在viewController使用

代码语言:javascript
复制
- (IBAction)clickBtn:(id)sender {
    
    [self performSelector:@selector(play)];
}

void play(id self, SEL _cmd) {
    NSLog(@"动态添加成功");
    UIViewController *vc = [UIViewController new];
    vc.view.backgroundColor = [UIColor redColor];
    [self presentViewController:vc animated:YES completion:nil];
}

+ (BOOL)resolveInstanceMethod:(SEL)aSEL
{
    if (aSEL ==  NSSelectorFromString(@"play")) {
        class_addMethod(self, aSEL, (IMP) play, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:aSEL];
}
六、实现字典和模型的自动转换

获取属性的列表的方法是字典转模型的比较核心的方法

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017.10.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、动态交换方法
  • 二、为分类添加属性
  • 三、获取实例变量列表
  • 四、实现NSCoding的自动归档和自动解档;
  • 五、动态添加方法
    • 六、实现字典和模型的自动转换
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档