OC学习9——反射机制
1、OC提供了3种编程方式与运行环境进行交互:
- 直接通过OC的源代码:这是最常见的方式,开发人员只是编写OC源代码,而运行环境负责在后台工作。
- 通过NSObject类中定义的方法进行动态编程:因为绝大部分类都是NSObject的子类(NSProxy例外),所以绝大部分对象都继承了NSObject的方法,因此,所有对象都可以调用NSObject的方法来编程。例如NSObject提供了isKindOfClass:(判断指定类及其子类的实例对象)、isMemberOfClass:(判断指定类的实例对象)方法用于判断该对象所属的类;respondsToSelector:用于判断该实例是否可调用指定方法;methodForSelector:则用于返回指定方法的指针。
- 直接调用运行时函数进行动态编程:运行时系统是一个动态共享库,有一些列位于usr/include/objc目录的头文件中的函数和数据结构组成,这些工具都是C风格,他们并不是OC必需的,但有些函数在OC编程中也是有用的。
2、OC中同样也提供了与Java中类似的反射机制,这种动态变成机制可以让OC语言更加灵活。说到反射,首先我们要弄清楚什么是反射,反射的定义是运行中的程序检查自己和软件运行环境的能力,它可以根据它发现的进行改变。通俗的讲就是反射可以在运行时根据指定的类名获得类的信息。
3、为什么要用反射,也就是反射的意义何在?
- 当你做一个软件可以安装插件的功能,你连插件的类型名称都不知道,你怎么实例化这个对象呢?因为程序是支持插件的(第三方的),在开发的时候并不知道。所以,无法在代码中 New出来,但反射可以,通过反射,动态加载程序集,然后读出类,检查标记之后再实例化对象,就可以获得正确的类实例。反射的目的就是为了扩展未知的应用。比如你写了一个程序,这个程序定义了一些接口,只要实现了这些接口的dll都可以作为插件来插入到这个程序中。那么怎么实现呢?就可以通过反射来实现。就是把dll加载进内存,然后通过反射的方式来调用dll中的方法。很多工厂模式就是使用的反射。
- 在编码阶段不知道那个类名,要在运行期从配置文件读取类名,这时候就没有办法硬编码,new ClassName(),而必须用到反射才能创建这个对象。
4、每个类都有一个对应的Class,Class对象其实本质上就是一个结构体,这个结构体中的成员变量还是自己,这种设计方式非常像链表的数据结构。然后通过一个类的Class可以实现获取该类的实例变量、方法等信息,从而可以实现创建对象和调用方法的目的。OC中获得Class通常有3种方法:
- 使用Class NSClassFromString(NSString * aClassName)函数来获取Class,该函数需要传入字符串参数,该字符串的值是某个类的类名。
- 调用某个类的class方法来获取该类对应的Class,例如 [FKPerson class]; 将会返回FKPerson类对应的Class
- 调用某个对象的class方法,该方法是NSObject类中的实例方法,所以所有的OC对象都可以调用该方法,该方法将会返回该对象所属类对应的Class。
#import <Foundation/Foundation.h> int main(int argc, char * argv[])
{
@autoreleasepool{
//通过字符串来获取Class
Class clazz = NSClassFromString(@"NSDate") ;
NSLog(@"%@", clazz) ; //直接使用Class来创建对象
id date = [[clazz alloc] init] ;
NSLog(@"%@", date) ; //通过类来获取Class
NSLog(@"%d", clazz == NSDate.class) ; //通过对象来获取Class
NSLog(@"%@", [date class]) ;
}
}输出结果是NSDate、2017-08-15 13:15:34 +0000、1、_NSDate。其中最后一个返回的是_NSDate而不是NSDate的原因是因为OC中很多设计都是才用的类簇的设计,NSDate只是这个类簇的前端,当程序调用[[NSDate alloc] init] 创建对象时,程序实际返回的只是NSDate的子类(_NSDate)的实例,而不是NSDate的实例。因此直接调用date来获取Class时返回的是_NSDate。
5、程序中在才用反射机制创建类时一般都需要对创建的对象或者待反射的对象进行一个继承或从属关系的检查,即需要判断一个对象是否是某个类的实例或者是否是某个类或者其子类的实例。闫完成这样的工作,我们可以直接调用NSObject提供的如下方法进行判断:
- isKindOfClass:该方法需要传入一个Class参数,判断是否是指定类及其子类的实例对象
- isMemberOfClass:该方法也需要传入一个Class参数,判断是否是指定类的实例对象
- conformsToProtocol:该方法需要传入一个Protocol参数,为了在程序中获取Protocol对象,通常通过两种方法来获取
- 使用@Protocol(协议名)来实现
- 可调用Protocol * NSProtocolFromString(NSString *namestr);函数来根据协议名字符串来获取对应的协议
6、如果程序需要动态地调用对象的setter、getter方法,则可通过OC提供的KVC机制来实现。如果程序需要访问对象的实例变量的值,那么不管这个实例变量是否在类的接口部分定义,也不管该变量使用哪种访问控制符修饰,或者是否在类的实现部分定义,程序都可通过KVC机制来设置、访问实例变量的值。(具体KVC机制的原理后面学习了再补充:OC学习篇之---KVC和KVO操作)
7、如果程序需要判断某个对象是否可调用方法,则可通过NSObject的如下方法进行判断:
- respondsToSelector:该方法需要传入一个SEL参数——OC把方法称为选择器,因此,OC使用SEL对象来代表方法。如果该对象可调用该方法,则返回YES,否则返回FALSE。
如果程序需要动态调用对象的普通方法,则可以通过如下两种方式来实现:
- 通过NSObject提供的系列performSelector:方法来实现,该方法第一个参数需要传入一个SEL对象,如果调用方法需要传入参数,则还可以通过withObject:标签来传入参数
- 使用objc_msgSend(receiver, selector, ...)函数来调用。该函数第一个参数是方法调用者,第二个参数代表调用的方法,接下来的参数将作为调用方法的参数。
为了在程序中动态获得SEL对象,OC提供了如下两种方法来获得:
- 使用@selector指令来获取房前类中制定的方法,该指令需要用完整的方法签名关键字作为参数,仅有方法名是不够的
- 使用SEL NSSelectorFromString(NSString * aSelectorName)函数根据方法签名关键字的字符串获取对应的方法
#import <Foundation/Foundation.h> //定义接口部分
@interface FKCar : NSObject
@end #import <objc/message.h>
#import "FKCar.h" //实现部分
@implementation FKCar
- (void) move : (NSString *) count
{
int num = [count intValue] ;
for(int i = ; i < num ; i++)
{
NSLog(@"%@", [NSString stringWithFormat:@"汽车正在路上走~~~%d", i]);
}
} - (double) addSpeed: (double) factor
{
//此处希望能动态调用move:方法
//使用performSelector:动态调用move:方法,通过@selector指令来获取SEL对象
[self performSelector:@selector(move:) withObject: [NSNumber numberWithInt:]] ; //使用performSelector:动态调用move:方法,通过NSSelectorFromString函数来获取SEL对象
[self performSelector:NSSelectorFromString(@"move:") withObject: [NSNumber numberWithInt:]] ; //使用objc_msgSend()函数动态调用move:方法,通过@selector指令来获取SEL对象
objc_msgSend(self, @selector(move:) , [NSNumber numberWithInt:]) ; //使用objc_msgSend()函数动态调用move:方法,通过NSSelectorFromString函数来获取SEL对象
objc_msgSend(self, NSSelectorFromString(@"move:") , [NSNumber numberWithInt:]) ; NSLog(@"正在加速。。。%g", factor) ;
return * factor ;
} @end
8、此外,NSObject还提供了如下一个方法
- - (IMP)methodForSelector:(SEL) aSelector:该方法根据传入的SEL参数,返回该方法对应的IMP对象。
IMP代表指向OC方法的函数指针,OC方法的本质还是函数,IMP相当于一个指向函数的指针变量,也就说代表了函数的入口,接下来就可以通过IMP来调用该函数——也就是调用OC的方法。对于一个指向OC方法的函数指针变量,它指向的函数签名的第一个参数通常是方法的调用者,第二个参数通常是方法对应的SEL对象,接下来的参数就说调用该方法的参数。因此,通常会使用如下的代码格式来定义指向OC方法的函数指针,第一个id形参表示方法调用者,第二个SEL类型代表方法,接下来可以声明调用该方法所需的参数:
返回值类型 (* 指针变量名) (id,SEL,。。。)
#import <Foundation/Foundation.h>
#import "FKCar.h" int main(int argc, char * argv[])
{
@autoreleasepool{ //获取FKCar类
Class clazz = NSClassFromString(@"FKCar") ; //动态创建对象
id car = [[clazz alloc] init] ; //使用performSelector:方法来动态调用方法
[car performSelector: @selector(addSpeed:) withObject: [NSNumber numberWithDouble: 3.4]] ; //使用objc_msgSend:方法动态调用方法
objc_msgSend(car, @selector(addSpeed:), 3.4) ; //定义函数指针变量
double (* addSpeed) (id, SEL, double) ; //获取car对象的addSpeed:方法,并将该方法赋给addSpeed函数指针变量
addSpeed = (double (* addSpeed) (id, SEL, double)) [car methodForSelector: NSSectorFromString(@"addSpeed:")] ; //通过addSpeed函数指针变量来调用car对象的方法
double speed = addSpeed(car , @selector(addSpeed:), 2.4) ; //输出
NSLog(@"加速后的速度为:%g", speed) ;
}
}
9、在开发了大量的IOS项目之后,就会发现有大量的代码是类似的,如果能把这些通用代码抽取成更通用的框架,那么程序将会拥有ugenghaode框架——当需要开发出那些具有通用性质的框架时,这些框架代码无法预先知道被调用组件的实现类,以及具有那些方法,这些信息可能是通过配置文件给出的,而这些诶框架必须动态地根据字符串来创建对象,根据字符创来决定调用那个方法,这些功能呢个则必须借助OC的反射、动态机制来实现,这也回到了我们前面讲的为什么要用反射机制的原因。
OC学习9——反射机制的更多相关文章
- java学习之反射机制
java语言区别于C,C++等准静态语言的最大特点就是java的反射机制.静态语言的最直接定义就是不能在运行时改变程序结构或变量的类型.按照这样的定义,python,ruby是动态语言,C,C++,J ...
- oc中的反射机制
好久没有总结过了,一直在赶项目... 今天来总结一下OC中的反射机制,有什么不对的地方,还请多多海涵. 反射机制,简单的说就是在程序运行期间通过类的名字来动态的获取类的信息,从而实现动态的创建类,以及 ...
- Java学习之反射机制及应用场景
前言: 最近公司正在进行业务组件化进程,其中的路由实现用到了Java的反射机制,既然用到了就想着好好学习总结一下,其实无论是之前的EventBus 2.x版本还是Retrofit.早期的View注解框 ...
- java学习--Reflection反射机制
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...
- Java反射学习:深入学习Java反射机制
一.Java反射的理解(反射是研究框架的基础之一) Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的 ...
- java反射学习之一反射机制概述
一.反射机制背景概述 1.反射(reflection)是java被视为动态语言的一个关键性质 2.反射机制指的是程序在运行时能获取任何类的内部所有信息 二.反射机制实现功能概述 1.只要给定类的全名, ...
- Java 学习之反射机制“解刨”分解类,并获取内容!
正常情况下,单纯的做开发是接触不到反射机制的(额,当然并不排除例外的情况了).下面我就对我学到的反射方面的知识做一个小小的总结,旨在复习和以后的查看. 原理分析: 所谓反射就是将一个类当做我们研究的对 ...
- JAVA 学习笔记 - 反射机制
1. JAVA反射机制的概念 2. 怎样实例化一个 Class对象 Class.forName(包名.类名); 对象.getClass(); 类.class; ================== ...
- Java学习:反射机制简介
反射机制是什么 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法: 对于任意一个对象,都能够调用它的任意一个方法和属性: 这种动态获取的信息以及动态调用对象的方法的功能称为ja ...
随机推荐
- [模拟]P1202 [USACO1.1]黑色星期五Friday the Thirteenth
原题 解析: 坑 其实.样例的部分是从周六~周五输出的,习惯不同吧..这里考虑到从这个月的13号到下一个月的13号所花天数为这个月的天数,然后愉快的判断一下闰年即可.这里的周一~周日编号为0~6,一月 ...
- ListView ,recycleView列表带进度条
实现上图功能有两种思路. 一:普通做法,更新item的数据,不停调用notifydatachange ; 二:各管自家刷新. 一个下载对应一个下载线程.线程持有对应item在Listview中的位置. ...
- python学习笔记 list
1.list中的任一元素可以是任一类型.可以是混合的,如,前两个字符串后面的是数字.都是可以的. 2.可以用-1表示最后一个元素. 3.注意不要越界. 4.len(mates) 用来计算list的大小 ...
- 关于laravel 用paginate()取值取不到的问题
前几天在写api的时候,出现了一个比较奇怪的问题,用paginate()方法取值取不到的问题,我奇怪的是,我用paginate()方法取值是直接复制粘贴之前自己写过的api中的代码的,怎么突然取不到了 ...
- WPF开发的彩票程序(练手好例子) 附源码
前言 WPF是.NET最新的界面开发库,开发界面非常灵活!但是学习WPF难度也非常大. 应朋友之邀,编写了一个小程序.程序虽小,五脏俱全,WPF开发的灵活性可窥见一斑. 对于新手学习有很好的借鉴意义, ...
- Hive详解
1. Hive基本概念 1.1 Hive简介 1.1.1 什么是Hive Hive是基于Hadoop的一个数据仓库工具,可以将结构化的数据文件映射为一张数据库表,并提供类SQL查询功能. 1.1 ...
- 网站图片挂马检测及PHP与python的图片文件恶意代码检测对比
前言 周一一早网管收到来自阿里云的一堆警告,发现我们维护的一个网站下有数十个被挂马的文件.网管直接关了vsftpd,然后把警告导出邮件给我们. 取出部分大致如下: 服务器IP/名称 木马文件路径 更新 ...
- openstack windows 2008镜像 制作
openstack windows 2008镜像 制作 openstack centos6 centos7 kvm 镜像制作 http://www.cnblogs.com/elvi/p/7922421 ...
- 为什么选择Django?
Web开发是Python语言应用领域的重要部分,也是工作岗位比较多的领域.如果你对基于Python的Web开发有兴趣,正打算开始学习使用Python做Web开发,或者已经是一个Web开发者有工作需要, ...
- React Native学习——动画Animated(笔记)
很多地方都需要用到动画,先看下文档吧. 一.两个互补的动画系统 LayoutAnimation:用于全局的布局动画 Animated:用于创建更精细的交互控制的动画(主要是这个) 二.Animated ...