Jerry's Blog

Less is more, Live and learning

Hello,I'm an iOS developer in China.


反射的概念及应用

前言

反射是指程序在运行时可以访问、检测、修改它本身状态或行为的一种能力。通常动态语言如ObjC具有这种特性。下面将介绍反射的特性以及在iOS中的应用举例。

使用反射可以做什么

反射的应用场景

  • 在运行时可以判断一个对象是否属于某个类、是否遵守某个协议、是否响应某个方法
  • 在运行时可以构造任意一个类、生成一个对象、得到一个方法
  • 在运行时动态调用一个方法

ObjC中的反射

由于ObjC的动态特性,下面方法可以在运行时根据字符串参数反射得到类、方法selector、协议方法等

// 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];

ObjC中的常用判断方法

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

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

反射的应用场景

需求:根据后台返回的参数,来动态跳转页面,并传参数

这个需求可以通过反射机制来实现,通过服务端下发的json对象动态的在运行时创建类,调用指定方法,并跳转之前页面,例如下例:

// 假设和后台约定格式如下:
NSDictionary *dict = @{
     // 类名
     @"className" : @"UserListViewController", 
     // 数据参数
     @"propertys" : @{ @"name": @"liuxiaozhuang", 
                       @"age": @3 },
     // 调用方法名
     @"method" : @"refreshUserInformation"
 };

下面通过反射机制简单实现了控制器跳转的方法,在实际使用中再根据业务需求进行修改即可

// 简单封装的页面跳转方法,只是做演示,代码都是没问题的,使用时可以根据业务需求进行修改。
- (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];
}

参考资料

iOS反射机制

最近的文章

App启动分析与优化策略

启动过程分析 解析Info.plist 加载相关信息,例如闪屏 沙箱建立、权限检查 Mach-O加载 Mach-O 文件:我们写的程序想要跑起来,肯定它的可执行文件格式要被操作系统所理解。比如ELF是Linux下的可执行文件格式,那么对于OS X / iOS来说,Mach-O 是其可执行文件格式。 Mach-O格式主要包括以下几种文件类型: Executable:应用的主要二进...…

iOS继续阅读
更早的文章

AppDelegate如何瘦身?

前言AppDelegate瘦身指的就是对AppDelegate类进行解耦和拆分。AppDelegate 作为整个应用程序的代理对象,处理应用程序的生命周期、根视图控制器初始化、应用级URL跳转、推送通知接收、网络配置、及其他第三方SDK初始化等工作。为了让AppDelegate内部代码简洁,逻辑清晰,则需要对其进行整理拆分。下面有几种瘦身方式:模块化管理通过一个模块化管理类,将AppDelegate中的职责拆分至各个模块中去处理,简化了AppDelegate内部代码。FRDModuleMa...…

iOS继续阅读