在开发过程中我们经常会遇到对接推送,推送的类型可能有很多种
1、接到推送,我们跳转到推送内容的详情页面
2、接到推送,我们跳转到一个网页
......
还有很多的情况,这个时候我们就要根据数据类型的不同进行判断,但是如果页面有很多我们就要写一大堆if else判断或switch判断。 这样做就显得很low了,而且不是非常的灵活了,而且后续如果我们增加其他页面的跳转就更不好处理了。
这个时候我们就需要Runtime来实现。
首先,我们要和后台定义后一个规则,这个规则不要去轻易变动
例如:
@{
// 类名
@"className" : @"DemoViewController",
// 数据参数
@"propertys" : @{ @"name": @"test",
@"age": @3 },
// 调用方法名
@"method" : @"refreshUserInformation"
};
真时候我们就要创建很多个类似DemoViewController的类
#import <UIKit/UIKit.h>
// 由于使用的KVC赋值,如果不想把这两个属性暴露出来,把这两个属性写在.m文件也可以
@interface DemoViewController : UIViewController
@property (nonatomic,strong) NSString *name;/*!< 用户名 */
@property (nonatomic,strong) NSNumber *age;/*!< 用户年龄 */
/** 使用反射机制反射为SEL后,调用的方法 */
- (void)refreshUserInformation;
@end
这个时候如果后台来一条消息我们要如何操作呢?这个时候我们就要正式使用runtime
/**
* 跳转界面
*/
- (void)push:(NSDictionary *)params{
// 类名
NSString *class =[NSString stringWithFormat:@"%@", params[@"class"]];
const char *className = [class cStringUsingEncoding:NSASCIIStringEncoding];
// 从一个字串返回一个类
Class newClass = objc_getClass(className);
if (!newClass) {
// 创建一个类
Class superClass = [NSObject class];
newClass = objc_allocateClassPair(superClass, className, 0);
// 注册你创建的这个类
objc_registerClassPair(newClass);
}
// 创建对象
id instance = [[newClass alloc] init];
NSDictionary *propertys = params[@"propertys"];
[propertys enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// 检测这个对象是否存在该属性
if ([self checkIsExistPropertyWithInstance:instance verifyPropertyName:key]) {
// 利用kvc赋值
[instance setValue:obj forKey:key];
}
}];
// 获取导航控制器
UITabBarController *tabVC = (UITabBarController *)self.window.rootViewController;
UINavigationController *pushClassStance = (UINavigationController *)tabVC.viewControllers[tabVC.selectedIndex];
// 跳转到对应的控制器
[pushClassStance pushViewController:instance animated:YES];
NSDictionary *methods = params[@"method"];
[methods enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
// 检测这个对象是否存在该属性
if ([self checkIsExistMethodWithInstance:instance verifyPropertyName:methods[key]]) {
// 从字典中获取方法名,并调用对应的方法
SEL selector = NSSelectorFromString(methods[key]);
[instance performSelector:selector];
}
}];
}
检测这个类有没有这个属性
/**
* 检测对象是否存在该属性
*/
- (BOOL)checkIsExistPropertyWithInstance:(id)instance verifyPropertyName:(NSString *)verifyPropertyName
{
unsigned int outCount, i;
// 获取对象里的属性列表
objc_property_t * properties = class_copyPropertyList([instance
class], &outCount);
for (i = 0; i < outCount; i++) {
objc_property_t property =properties[i];
// 属性名转成字符串
NSString *propertyName = [[NSString alloc] initWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
// 判断该属性是否存在
if ([propertyName isEqualToString:verifyPropertyName]) {
free(properties);
return YES;
}
}
free(properties);
return NO;
}
检测这个类有没有这个方法
/**
* 检测对象是否存在该方法
*/
- (BOOL)checkIsExistMethodWithInstance:(id)instance verifyPropertyName:(NSString *)verifyMethodName
{
unsigned int outCount, i;
Method *methods = class_copyMethodList([instance class], &outCount);
for ( i = 0 ; i < outCount; i++ ) {
Method method = methods[i];
SEL sel = method_getName(method);
const char *name = sel_getName(sel);
NSString *methodName = [NSString stringWithFormat:@"%s",name];
// 判断该属性是否存在
if ([methodName isEqualToString:verifyMethodName]) {
free(methods);
return YES;
}
}
free(methods);
return NO;
}
只要我们在App侧做好了对应的类,属性、方法等,我们就可以通过服务器返回规定好的数据进行跳转。我们这里也不需要写大量代码去维护还加if else、switch等判断
代码的灵活度更高