Objective-C 反射机制

该文章属于<简书 — 刘小壮>原创,特此感谢:<简书 — 刘小壮> http://www.jianshu.com/p/5bbde2480680

了解反射机制

Objective-C语言中的OC对象,都继承自NSObject类。这个类为我们提供了一些基础的方法和协议,我们可以直接调用从这个类继承过来方法。当然,本篇文章中讲到的反射方法,就在NSObjectFoundation框架中。

反射机制涉及到的东西比较多,这篇文章只从OC层面来讲反射机制,不涉及runtime部分,以后会写文章来专门讲runtime的。

获取Class对象

Class对象其实本质上就是一个结构体,这个结构体中的成员变量还是自己,这种设计方式非常像链表的数据结构。

typedef struct objc_class *Class;
struct objc_class {Class isa  OBJC_ISA_AVAILABILITY;                                  
}

可以直接用一个实例对象或类对象,直接调用Class方法,都可以获取Class对象。我们调用下面三个方法,都可以获得Class对象。

// 在实例方法中通过self调用class实例方法获取类对象
[self class]
// 通过ViewController类直接调用class类方法获取类对象
[ViewController class]
// 在类方法中使用类对象调用class方法获取类对象
+ (Class)classMethod {return [self class];
}

通过打印,我们发现调用这三个方法,获取到的类对象是同一个类对象,内存地址也是一样的。
这是因为这三个方法调用class方法,打印的都是类对象的isa指针。

NSLog(@"%p, %p, %p", [ViewController classMethod], [ViewController class], [self class]);
打印结果:0x10c68e978, 0x10c68e978, 0x10c68e978
反射方法

系统Foundation框架为我们提供了一些方法反射的API,我们可以通过这些API执行将字符串转为SEL等操作。由于OC语言的动态性,这些操作都是发生在运行时的。

// SEL和字符串转换
FOUNDATION_EXPORT NSString *NSStringFromSelector(SEL aSelector);
FOUNDATION_EXPORT SEL NSSelectorFromString(NSString *aSelectorName);
// Class和字符串转换
FOUNDATION_EXPORT NSString *NSStringFromClass(Class aClass);
FOUNDATION_EXPORT Class __nullable NSClassFromString(NSString *aClassName);
// Protocol和字符串转换
FOUNDATION_EXPORT NSString *NSStringFromProtocol(Protocol *proto) NS_AVAILABLE(10_5, 2_0);
FOUNDATION_EXPORT Protocol * __nullable NSProtocolFromString(NSString *namestr) NS_AVAILABLE(10_5, 2_0);

通过这些方法,我们可以在运行时选择创建那个实例,并动态选择调用哪个方法。这些操作甚至可以由服务器传回来的参数来控制,我们可以将服务器传回来的类名和方法名,实例为我们的对象。

// 假设从服务器获取JSON串,通过这个JSON串获取需要创建的类为ViewController,并且调用这个类的getDataList方法。
Class class = NSClassFromString(@"ViewController");
ViewController *vc = [[class alloc] init];
SEL selector = NSSelectorFromString(@"getDataList");
[vc performSelector:selector];
常用判断方法

NSObject类中为我们提供了一些基础方法,用来做一些判断操作,这些方法都是发生在运行时动态判断的。

// 当前对象是否这个类或其子类的实例
- (BOOL)isKindOfClass:(Class)aClass;
// 当前对象是否是这个类的实例
- (BOOL)isMemberOfClass:(Class)aClass;
// 当前对象是否遵守这个协议
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
// 当前对象是否实现这个方法
- (BOOL)respondsToSelector:(SEL)aSelector;

下面的代码是判断当前对象是否是UIView对象或其子类,其它方法使用和下面类似。

if ([self isKindOfClass:NSClassFromString(@"UIView")]) {NSLog(@"The Current Class is UIView Class");
}

反射机制使用技巧

假设有一天公司产品要实现一个需求:根据后台推送过来的数据,进行动态页面跳转,跳转到页面后根据返回到数据执行对应的操作。

遇到这样奇葩的需求,我们当然可以问产品都有哪些情况执行哪些方法,然后写一大堆if else判断或switch判断。
但是这种方法实现起来太low了,而且不够灵活,假设后续版本需求变了,还要往其他已有页面中跳转,这不就傻眼了吗....

这种情况反射机制就派上用场了,我们可以用反射机制动态的创建类并执行方法。当然也可以通过runtime来实现这个功能,但是我们当前需求反射机制已经足够满足需求了,如果遇到更加复杂的需求可以考虑用runtime来实现。

这时候就需要和后台配合了,我们首先需要和后台商量好返回的数据结构,以及数据格式、类型等,返回后我们按照和后台约定的格式,根据后台返回的信息,直接进行反射和调用即可。

假设和后台约定格式如下:

@{// 类名@"className" : @"UserListViewController", // 数据参数@"propertys" : @{ @"name": @"liuxiaozhuang", @"age": @3 },// 调用方法名@"method" : @"refreshUserInformation"};

定义一个UserListViewController类,这个类用于测试,在实际使用中可能会有多个这样的控制器类。

#import <UIKit/UIKit.h>
// 由于使用的KVC赋值,如果不想把这两个属性暴露出来,把这两个属性写在.m文件也可以
@interface UserListViewController : UIViewController
@property (nonatomic,strong) NSString *name;/*!< 用户名 */
@property (nonatomic,strong) NSNumber *age;/*!< 用户年龄 */
/** 使用反射机制反射为SEL后,调用的方法 */
- (void)refreshUserInformation;
@end

下面通过反射机制简单实现了控制器跳转的方法,在实际使用中再根据业务需求进行修改即可。因为这篇文章主要是讲反射机制,所以没有使用runtime代码。

// 简单封装的页面跳转方法,只是做演示,代码都是没问题的,使用时可以根据业务需求进行修改。
- (void)remoteNotificationDictionary:(NSDictionary *)dict {// 根据字典字段反射出我们想要的类,并初始化控制器Class class = NSClassFromString(dict[@"className"]);UIViewController *vc = [[class alloc] init];// 获取参数列表,使用枚举的方式,对控制器属性进行KVC赋值NSDictionary *parameter = dict[@"propertys"];[parameter enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {// 在属性赋值时,做容错处理,防止因为后台数据导致的异常if ([vc respondsToSelector:NSSelectorFromString(key)]) {[vc setValue:obj forKey:key];}}];[self.navigationController pushViewController:vc animated:YES];// 从字典中获取方法名,并调用对应的方法SEL selector = NSSelectorFromString(dict[@"method"]);[vc performSelector:selector];
}

 

转载于:https://www.cnblogs.com/WJJ-Dream/p/5788415.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/356855.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java 模块化osgi_OSGi简介–模块化Java

java 模块化osgiOSGi联盟是这种搁浅的管理机构&#xff0c;它始于1999年。其最初目标是为网络设备创建开放搁浅。 基于此思想&#xff0c;此规范也针对Java引入。 Eclipse在Java中是第一个。 他们于2004年6月推出了基于OSGi的Eclipse IDE。 OSGi是在Java中定义动态模块的方法。…

java成绩查询_JavaWeb项目第三次总结_成绩查询的实现

查询图书的功能实现如何知道浏览器往服务器传入的参数1、在编写好查询页面后&#xff0c;使用火狐浏览器的friebug (全部—>POST—>参数)2、编写GradeListServlet&#xff0c;重写doGet()和doPOST()方法服务器处理页面请求的过程1、重写doPOST()方法。处理过程1、连接数据…

Train Problem I hdu 1022(栈)

http://acm.split.hdu.edu.cn/showproblem.php?pid1022 题意&#xff1a;给出火车的进站与出站顺序&#xff0c;判断是否可以按照给出的出站顺序出站。 #include <iostream> #include <stdio.h> #include <string.h> #include <string> #include <…

Spring数据和Redis

本文是我们学院课程的一部分&#xff0c;标题为Redis NoSQL键值存储 。 这是Redis的速成班。 您将学习如何安装Redis并启动服务器。 此外&#xff0c;您将在Redis命令行中乱七八糟。 接下来是更高级的主题&#xff0c;例如复制&#xff0c;分片和集群&#xff0c;同时还介绍了…

java的原生数据类型_Java中的8种原生数据类型(Primitive Data Types)分析

八种数据类型类型 int short long byte float double char boolean字节数 4 2 8 1 4 8 4 JVM相关大小 -2147483648~2147483647 -32768~32767 -9223372036854775808~9223372036854775807 -128~127 3.40282347E38F 1.79769313486231570E308tip:八种类型我是这样记忆的&#xff1a…

史上最全的SpringMVC学习笔记

SpringMVC学习笔记---- 一、SpringMVC基础入门&#xff0c;创建一个HelloWorld程序 1.首先&#xff0c;导入SpringMVC需要的jar包。 2.添加Web.xml配置文件中关于SpringMVC的配置<!--configure the setting of springmvcDispatcherServlet and configure the mapping-->&…

python 进程池 等待数量_【2020Python修炼记】python并发编程(六)补充—进程池和线程池...

1、2、 为啥要有 进程池和线程池进程池来控制进程数目&#xff0c;比如httpd的进程模式&#xff0c;规定最小进程数和最大进程数3、创建进程池的类pool如果指定numprocess为3&#xff0c;则进程池会从无到有创建三个进程&#xff0c;然后自始至终使用这三个进程去执行所有任务&…

shader weaver_Oracle通过邀请Weaver和Chin推动JavaFX向前发展

shader weaver昨天&#xff0c;我发布了愚人节帖子&#xff0c;内容涉及加入NASA协助探索红色大行星。 那篇文章与事实相距不远……美国宇航局开发的技术的所有细节都是100&#xff05;准确的。 哎呀&#xff0c;即使我辞职也是事实&#xff01; 唯一不正确的部分是我将加入的公…

mysql快速上手3

上一章给大家说的是数据库的视图&#xff0c;存储过程等等操作&#xff0c;这章主要讲索引&#xff0c;以及索引注意事项&#xff0c;如果想看前面的文章&#xff0c;url如下&#xff1a; mysql快速上手1mysql快速上手2索引简介 索引是对数据库表中一个或多个列&#xff08;例如…

python图像锐化_Python图像处理介绍--图像模糊与锐化

欢迎关注 “小白玩转Python”&#xff0c;发现更多 “有趣”引言在之前的文章中&#xff0c;我们讨论了边缘检测内核。在本文我们将讨论如何在图像上应用模糊与锐化内核&#xff0c;将这些内核应用到彩色图像上&#xff0c;同时保留核心图像。一如既往&#xff0c;我们从导入所…

看一下CDI 2.0 EDR1

CDI是最近对Java EE最好的补充之一。 该观点在用户和集成商之间广泛共享。 当前版本的CDI 1.2于2014年4月发布。现在&#xff0c;在2015年中期&#xff0c;我们将面对CDI 2.0规范的早期草案。 CDI 2.0将在Java 8和更高版本上运行。 最终版本计划于2016年发布&#xff0c;该路线…

redis key设计技巧

1: 把表名转换为key前缀 如, user: 2: 第2段放置用于区分区key的字段--对应mysql中的主键的列名,如userid 3: 第3段放置主键值,如2,3,4...., a , b ,c 4: 第4段,写要存储的列名 用户表 user , 转换为key-value存储 userid username passworde email 9 Lisi 1111111 li…

python编程游戏代码 游戏人物如何升级_如何玩游戏提高python编程技能?

笔者公众号&#xff1a;技术杂学铺Python Challenge是首个谜语编程网站。挑战者需要连续挑战33个需要编程才能解开的谜题以锻炼自己的编程能力。该网站建立于2005年&#xff0c;至今已经有三百多万位访客浏览并尝试解决该网站的谜题。python challenge 官网虽然网站建立的年代比…

Eclipse配置初始化(自用)

以上都是性能调优的配置&#xff0c;下面是其他常用的配置和优化 设置utf-8编码 window -> preferences -> General -> workplace中text file encoding改为utf-8 设置properties文件编码window -> preferences -> General -> Content Types —> Text —&g…

java bits_一段关于JAVA程序升级的问题(Changing bits)

展开全部import java.awt.Color;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import javax.swing.BorderFactory;import javax.swing.JButton;import javax.swing.JFrame;import javax.swing.JLabel;import javax.swing.JTextField;import javax.…

hibernate脏数据_Hibernate性能提示:脏收集效果

hibernate脏数据在使用Hibernate作为ORM开发服务器和嵌入式应用程序8年后&#xff0c;我全力以赴地寻求提高Hibernate性能的解决方案&#xff0c;阅读博客和参加会议&#xff0c;我决定与您分享在这些年中获得的知识。 这是更多新帖子中的第一篇&#xff1a; 去年&#xff0c;…

有关struts2中用到 js 总结

1.js中取Struts2中的栈里的值 var current "${currentPage}"; 2.js 如何提交执行提交url连接 &#xff0c;以及 Struts中的url如何如何写 var current "${currentPage}"; location.href"showSeparatePageGoods.action?currentPage"current&q…

java合并两个有序链表_JS实现的合并两个有序链表算法示例

本文实例讲述了JS实现的合并两个有序链表算法。分享给大家供大家参考&#xff0c;具体如下&#xff1a;将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。示例&#xff1a;输入&#xff1a;1->2->4, 1->3->4输出&…

外墙设计模式示例

本文是我们名为“ Java设计模式 ”的学院课程的一部分。 在本课程中&#xff0c;您将深入研究大量的设计模式&#xff0c;并了解如何在Java中实现和利用它们。 您将了解模式如此重要的原因&#xff0c;并了解何时以及如何应用模式中的每一个。 在这里查看 &#xff01; 目录 …

8.25小结

1.导出csv文件 后台导出&#xff1a;CSVUtils工具类&#xff1a; /*** * CSV文件导出工具类* * author* reviewer*/ public class CSVUtils {public static File createCSVFile(List<Object> head, List<List<Object>>dataList, String outPutPath, String f…