iOS开发中runtime介绍
一.runtime简介
- RunTime简称运行时。OC就是
运行时机制
,也就是在运行时候的一些机制,其中最主要的是消息机制。 - 对于C语言,
函数的调用在编译的时候会决定调用哪个函数
。 - 对于OC的函数,属于
动态调用过程
,在编译的时候并不能决定真正调用哪个函数,只有在真正运行的时候才会根据函数的名称找到对应的函数来调用。 - 事实证明:
- 在编译阶段,OC可以
调用任何函数
,即使这个函数并未实现,只要声明过就不会报错。 - 在编译阶段,C语言调用
未实现的函数
就会报错。
- 在编译阶段,OC可以
- 使用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介绍相关推荐
- iOS开发中的Web应用概述
为了更好的阅读体验,建议阅读原文 插播广告 -- 几十行代码完成资讯类App多种形式内容页 HybridPageKit :一个针对资讯类App高性能.易扩展.组件化的通用内容页实现框架. 想和我一起全 ...
- ios开发中计算代码运算时间_iOS日历、日期、时间的计算
时间和日历的计算在iOS开发中经常看到,经常看到大家在百度,开源中国等搜索这些答案.今天写个简单的时间和日历有关的计算. 获取一个月的总天数 1.获取当月的天数 - (NSInteger)getNum ...
- IOS开发中UIBarButtonItem上按钮切换或隐藏实现案例
IOS开发中UIBarButtonItem上按钮切换或隐藏案例实现案例是本文要介绍的内容,这个代码例子的背景是:导航条右侧有个 edit button,左侧是 back button 和 add bu ...
- 浅谈iOS 开发中的界面通信
在任何的软件开发中都离不开界面与界面之间的通信,界面通信的最直接的方法就是界面传值. 在开发过程中我们在页面传值时我们通常使用的方法有:属性传值法,block传值法,代理传值法,以及单例传值法,通知传 ...
- iOS开发中的单元测试(三)——URLManager中的测试用例解析
本文转载至 http://www.cocoachina.com/cms/plus/view.php?aid=8088 此前,我们在<iOS开发中的单元测试(一)&(二)>中介绍 ...
- iOS开发中屏幕旋转(二)
Morris_ 2019.04.11 前面有总结过一些在开发中遇到的屏幕旋转的基础知识. 一.设置应用支持的转屏方向 设置方式 00x1 在TARGET->General->Deploym ...
- 如何深入理解 iOS 开发中的锁?
摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不 ...
- 理解:iOS开发中锁的实现原理
摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知识点(三):Lock.本文也不 ...
- 深入理解 iOS 开发中的锁
深入理解 iOS 开发中的锁 摘要 本文的目的不是介绍 iOS 中各种锁如何使用,一方面笔者没有大量的实战经验,另一方面这样的文章相当多,比如 iOS中保证线程安全的几种方式与性能对比.iOS 常见知 ...
最新文章
- 分享26个关于Java开发视频教程(免费下载)
- Machine.Config在哪里?
- 解决 Out of range value adjusted for column 'ID' at row 1
- JZOJ 5407. 【NOIP2017提高A组集训10.21】Deep
- Bug in Code CodeForces - 420C (计数,图论)
- centos6.4安装及升级gcc 4.8.2(已实践)
- 【机器学习】传统目标检测算法之DPM
- 华为云WeLink:智能工作空间,联接无限想象
- iPhone 14进入代工试产阶段:首款打孔屏iPhone要来了
- Windows C++ 获取当前文件夹下有几个文件
- druid 手动指定数据源_Spring Boot2 系列教程(二十)整合JdbcTemplate 多数据源
- LeetCode.M11.盛最多水的容器
- VB中关于UBOUND和LBOUND含义
- 《大数据: IDEA开发工具配置大全》
- 我的世界回连Center
- Kubernetes访问报错: No route to host
- 继领英后,又一家科技公司宣布离开中国!
- 联想miix325可以安装android,联想miix325怎么重装win10系统
- 【Python】Python的单双引号
- 微信小程序实现连接蓝牙设备跑步APP
热门文章
- Lesson 1 Hello World
- Dropout抑制过拟合与超参数选择
- python生成验证码的程序_Python基础篇生成4位随机验证码
- snipaste安装和使用_snipaste替代品 amp; linux截图解决方案-截图、贴图工具Flameshot...
- Deep Learning with PyTorch 必看教程集(4本)
- 操作系统源代码_计算机自制操作系统(八):仿生DOS操作系统源代码
- python和abap的关系_ABAP 一对多关系
- Html5游戏开发-145行代码完成一个RPG小Demo
- SpringCloud之高可用的分布式配置中心(Spring Cloud Config)(七)
- 数据库优化的几种方法