一.runtime简介

  • RunTime简称运行时。OC就是运行时机制,也就是在运行时候的一些机制,其中最主要的是消息机制。
  • 对于C语言,函数的调用在编译的时候会决定调用哪个函数
  • 对于OC的函数,属于动态调用过程,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。
  • 事实证明:
    • 在编译阶段,OC可以调用任何函数,即使这个函数并未实现,只要声明过就不会报错。
    • 在编译阶段,C语言调用未实现的函数就会报错。
  • 使用runtime之前需要对环境进行如下配置:Enable Strict 选择No。

二.runtime的作用

1.发送消息
    •    方法调用的本质,就是让对象发送消息。
    •    objc_msgSend,只有对象才能发送消息,因此以objc开头.
    •    使用消息机制前提,必须导入#import <objc/message.h>
    •    进入文件所在路径,在终端使用clang -rewrite-objc main.m 指令可查看最终生成代码。

1     // NSObject *objc = [NSObject alloc];
2     NSObject *objc = objc_msgSend([NSObject class], @selector(alloc));
3
4     // objc = [objc init];
5     objc = objc_msgSend(objc, @selector(init));
6
7     NSLog(@"%@",objc);

消息机制作用:【调用已知的私有方法】

例:Person类中有两个私有方法。

 1 #import <Foundation/Foundation.h>
 2
 3 @interface Person : NSObject
 4
 5 @end
 6
 7 @implementation Person
 8
 9 - (void)run:(NSInteger)meter
10 {
11     NSLog(@"跑了%ld米",meter);
12 }
13
14 - (void)eat
15 {
16     NSLog(@"吃东西");
17 }
18
19 @end

 1 #import <objc/message.h>
 2 #import "Person.h"
 3
 4 @interface ViewController ()
 5
 6 @end
 7
 8 @implementation ViewController
 9
10 - (void)viewDidLoad {
11     [super viewDidLoad];
12
13     //Person *p = [Person alloc];
14     Person *p = objc_msgSend([Person class], @selector(alloc));
15
16     //p = [p init];
17     p = objc_msgSend(p, @selector(init));
18
19     // 调用eat
20     //[p eat];
21     objc_msgSend(p, @selector(eat));
22
23     // runtime
24     // 方法编号后面开始,依次就是方法参数排序
25     // objc_msgSend(id self, SEL op, ...)
26     objc_msgSend(p, @selector(run:),20);
27 }

// 调用【类方法】的方式有两种
    // 第一种通过类名调用
    [Person eat];
    // 第二种通过类对象调用
    [[Person class] eat];
    
    // 用类名调用类方法,底层会【自动把类名转换成类对象调用】
    // 本质:让【类对象发送消息】
    objc_msgSend([Person class], @selector(eat));

说到这里,不得不问:对象如何找到对应的方法去调用?

回答这个问题,首先要清楚:方法保存到什么地方?--->对象方法保存到类中,类方法保存到元类(meta class)中。每一个类都有方法列表methodList。
    1.根据对象的isa指针去对应的类中查找方法。isa:判断去哪个类查找对应的方法 指向方法调用的类。
    2.根据传入的方法编号(SEL),才能在方法列表中找到对应方法Method(方法名)。
    3.根据方法名(函数入口)找到函数实现。

消息机制原理:对象根据【方法编号SEL】去映射表查找对应的方法实现。

2.交换方法

•    开发使用场景:系统自带的方法功能不能满足需求,给系统自带的方法扩展一些功能,并且保持原有的功能。
    •    方式一:继承系统的类,重写方法。
    •    方式二:使用runtime,交换方法。

需求:给imageNamed方法提供功能,每次加载图片就判断下图片是否加载成功。

// 步骤一:先搞个分类,定义一个能加载图片并且能打印的方法 +(UIImage *)wm_imageNamed:(NSString *)name;
    // 步骤二:交换imageNamed和wm_imageNamed的实现,就能调用imageNamed,间接调用wm_imageNamed的实现。

写一个UIImage+Image.h的分类:

1 #import <UIKit/UIKit.h>
2
3 @interface UIImage (Image)
4
5 // 给方法加前缀,与系统方法区分
6 // 加载图片
7 + (UIImage *)wm_imageNamed:(NSString *)name;
8
9 @end

 1 #import "UIImage+Image.h"
 2 #import <objc/message.h>
 3
 4 @implementation UIImage (Image)
 5
 6 // 加载类的时候调用,肯定只会调用一次
 7 + (void)load
 8 {
 9     // 交换方法实现wm_imageNamed,imageNamed
10
11     // 获取方法 Method:方法名
12     // 获取类方法
13     // class:获取哪个类方法
14     // SEL:方法编号
15     Method imageNameMethod = class_getClassMethod(self, @selector(imageNamed:));
16
17     Method wm_imageNameMethod = class_getClassMethod(self, @selector(wm_imageNamed:));
18
19     method_exchangeImplementations(imageNameMethod, wm_imageNameMethod);
20
21 }
22
23 // 加载图片
24 // 判断
25 + (UIImage *)wm_imageNamed:(NSString *)name
26 {
27    //这里调用wm_imageNamed:实际上是调用imageNamed:.
28    UIImage *image = [UIImage wm_imageNamed:name];
29
30     if (image == nil) {
31         NSLog(@"加载失败");
32     }
33
34     return image;
35 }
36 @end

外界使用:无需导入分类头文件,直接使用imageNamed:方法即可实现判断图片是否加载成功。

 1 #import "ViewController.h"
 2
 3 @interface ViewController ()
 4
 5 @end
 6
 7 @implementation ViewController
 8
 9 - (void)viewDidLoad {
10     [super viewDidLoad];
11
12     [UIImage imageNamed:@"123"];
13 }

3.动态添加方法

开发使用场景:如果一个类里面方法非常多,加载类到内存的时候比较耗费资源,需要给每个方法生成映射表。可以使用动态给某个类添加方法解决。

那么,为什么动态添加方法?

OC大多懒加载,有些方法可能很久不会调用,节省内存。例如:电商,视频,社交,收费项目:会员机制,只要会员才拥有这些功能。

在ViewController导入Person类的头文件,调用run:方法。Person类并没有run:方法的声明和实现。

 1 @implementation ViewController
 2
 3 - (void)viewDidLoad {
 4     [super viewDidLoad];
 5
 6     // _cmd:方法编号
 7     NSLog(@"%@ %@",self,NSStringFromSelector(_cmd));
 8
 9     Person *p = [[Person alloc] init];
10     // 默认person,没有实现run方法,可以通过performSelector调用,但是会报错。
11     // 动态添加方法就不会报错
12     [p performSelector:@selector(run:) withObject:@20];
13
14 }
15 @end

 1 #import <Foundation/Foundation.h>
 2
 3 @interface Person : NSObject
 4
 5 @end
 6
 7
 8 #import <objc/message.h>
 9
10 @implementation Person
11
12 // 定义函数
13 // 没有返回值,有参数
14 // 默认OC方法都有两个隐式参数,self,_cmd
15 void run(id self, SEL _cmd, NSNumber *meter) {
16     NSLog(@"跑了%@米",meter);
17 }
18
19 // 什么时候调用:当一个对象调用未实现的方法,会调用这个方法处理,并且会把对应的方法列表传过来.
20 // 作用:去解决没有实现方法,动态添加方法
21 + (BOOL)resolveInstanceMethod:(SEL)sel {
22
23     // 刚好可以用来判断,未实现的方法是不是我们想要动态添加的方法
24     if (sel == @selector(run:)) {
25         // 动态添加run:方法
26
27         // 第一个参数class:给哪个类添加方法
28         // 第二个参数SEL:添加方法的方法编号
29         // 第三个参数IMP:添加方法的函数实现(函数地址)
30         // 第四个参数type:函数的类型,(返回值+参数类型) v表示void;@表示对象->self;:表示SEL->_cmd 可以传nil
31         class_addMethod(self, sel, (IMP)run, "v@:");
32
33         return YES;
34     }
35     return [super resolveInstanceMethod:sel];
36 }
37 @end

class_addMethod说明,官方文档给出:

1.函数至少要有两个参数:self和_cmd。

2.关于第四个参数type,可以参照类型编码Type Encodings填写。

3.Type第二和第三个字符必须是@和:,第一个是函数返回值类型。(实测Type传nil也可以)

控制台打印信息:

2016-04-15 09:52:53.634 Runtime(动态添加方法)[38020:1362250] <ViewController: 0x7f9f69d21460> viewDidLoad
2016-04-15 09:52:53.634 Runtime(动态添加方法)[38020:1362250] 跑了20米

4.给分类添加属性

原理:给一个类声明属性,其实本质就是给这个类添加关联。
 
属性的本质:让属性与某个对象产生一段关联
 
使用场景:【给系统的类添加属性】

例:需求:给NSObject添加一个name属性,动态添加属性 -> runtime

新建分类NSObject+Property

 1 #import <Foundation/Foundation.h>
 2
 3 @interface NSObject (Property)
 4
 5 // @property在分类中作用:仅仅是生成get,set方法声明,必不会生成get,set方法实现和下划线成员属性
 6 @property NSString *name;
 7
 8 @end
 9
10
11 #import <objc/message.h>
12
13 @implementation NSObject (Property)
14
15 - (void)setName:(NSString *)name
16 {
17
18     // 保存name
19     // 动态添加属性 = 本质:让对象的某个属性与值产生关联
20     /*
21         object:保存到哪个对象中
22         key:用什么属性保存 属性名
23         value:保存值
24         policy:策略,strong,weak
25      */
26     objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
27
28 }
29
30 - (NSString *)name
31 {
32     return objc_getAssociatedObject(self, "name");
33 }
34 @end

分类NSObject+Property

 1 #import "ViewController.h"
 2 #import "Person.h"
 3 #import "NSObject+Property.h"
 4
 5 @interface ViewController ()
 6
 7 @end
 8
 9 @implementation ViewController
10
11 - (void)viewDidLoad {
12     [super viewDidLoad];
13
14     NSObject *objc = [[NSObject alloc] init];
15
16     objc.name = @"123";
17
18     NSLog(@"%@",objc.name);
19 }
20 @end

控制器ViewController

控制台打印信息:

2016-04-15 10:19:19.100 Runtime(给分类添加属性)[38433:1382316] 123

5.字典转模型

由于字典转模型内容较多,新开一个blog详情请点击:http://www.cnblogs.com/wm-0818/p/5394567.html

转载于:https://www.cnblogs.com/wm-0818/p/5391787.html

iOS开发中runtime介绍相关推荐

  1. iOS开发中的Web应用概述

    为了更好的阅读体验,建议阅读原文 插播广告 -- 几十行代码完成资讯类App多种形式内容页 HybridPageKit :一个针对资讯类App高性能.易扩展.组件化的通用内容页实现框架. 想和我一起全 ...

  2. ios开发中计算代码运算时间_iOS日历、日期、时间的计算

    时间和日历的计算在iOS开发中经常看到,经常看到大家在百度,开源中国等搜索这些答案.今天写个简单的时间和日历有关的计算. 获取一个月的总天数 1.获取当月的天数 - (NSInteger)getNum ...

  3. IOS开发中UIBarButtonItem上按钮切换或隐藏实现案例

    IOS开发中UIBarButtonItem上按钮切换或隐藏案例实现案例是本文要介绍的内容,这个代码例子的背景是:导航条右侧有个 edit button,左侧是 back button 和 add bu ...

  4. 浅谈iOS 开发中的界面通信

    在任何的软件开发中都离不开界面与界面之间的通信,界面通信的最直接的方法就是界面传值. 在开发过程中我们在页面传值时我们通常使用的方法有:属性传值法,block传值法,代理传值法,以及单例传值法,通知传 ...

  5. iOS开发中的单元测试(三)——URLManager中的测试用例解析

    本文转载至 http://www.cocoachina.com/cms/plus/view.php?aid=8088   此前,我们在<iOS开发中的单元测试(一)&(二)>中介绍 ...

  6. iOS开发中屏幕旋转(二)

    Morris_ 2019.04.11 前面有总结过一些在开发中遇到的屏幕旋转的基础知识. 一.设置应用支持的转屏方向 设置方式 00x1 在TARGET->General->Deploym ...

  7. 如何深入理解 iOS 开发中的锁?

    摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不 ...

  8. 理解:iOS开发中锁的实现原理

    摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不 ...

  9. 深入理解 iOS 开发中的锁

    深入理解 iOS 开发中的锁 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知 ...

最新文章

  1. 分享26个关于Java开发视频教程(免费下载)
  2. Machine.Config在哪里?
  3. 解决 Out of range value adjusted for column 'ID' at row 1
  4. JZOJ 5407. 【NOIP2017提高A组集训10.21】Deep
  5. Bug in Code CodeForces - 420C (计数,图论)
  6. centos6.4安装及升级gcc 4.8.2(已实践)
  7. 【机器学习】传统目标检测算法之DPM
  8. 华为云WeLink:智能工作空间,联接无限想象
  9. iPhone 14进入代工试产阶段:首款打孔屏iPhone要来了
  10. Windows C++ 获取当前文件夹下有几个文件
  11. druid 手动指定数据源_Spring Boot2 系列教程(二十)整合JdbcTemplate 多数据源
  12. LeetCode.M11.盛最多水的容器
  13. VB中关于UBOUND和LBOUND含义
  14. 《大数据: IDEA开发工具配置大全》
  15. 我的世界回连Center
  16. Kubernetes访问报错: No route to host
  17. 继领英后,又一家科技公司宣布离开中国!
  18. 联想miix325可以安装android,联想miix325怎么重装win10系统
  19. 【Python】Python的单双引号
  20. 微信小程序实现连接蓝牙设备跑步APP

热门文章

  1. Lesson 1 Hello World
  2. Dropout抑制过拟合与超参数选择
  3. python生成验证码的程序_Python基础篇生成4位随机验证码
  4. snipaste安装和使用_snipaste替代品 amp; linux截图解决方案-截图、贴图工具Flameshot...
  5. Deep Learning with PyTorch 必看教程集(4本)
  6. 操作系统源代码_计算机自制操作系统(八):仿生DOS操作系统源代码
  7. python和abap的关系_ABAP 一对多关系
  8. Html5游戏开发-145行代码完成一个RPG小Demo
  9. SpringCloud之高可用的分布式配置中心(Spring Cloud Config)(七)
  10. 数据库优化的几种方法