iOS runtime 之跳转页面以及动态添加属性

序言

runtime 的主要用处有以下几点:

  • 给分类添加属性
  • 消息转发机制
  • 动态交换方法的实现
  • 手动实现多继承(oc本身是不支持多继承的)

今天主要是用runtime实现页面跳转以及动态添加属性,主要应用于手机App通过推送过来的数据内容来跳转不同的界面,并把界面数据展示出来或者是手机内部根据不同的cell的点击事件,不同的数据跳转不同的界面

原理

通过数据中带的Class类名来判断是否存在这个类
存在:直接通过Class实例化,KVC绑定一下数据源,PushController里面
不存在:动态创建Class类以及变量,实例化后通过KVC绑定一下数据源,PushController里面

主要方法

//创建Class
   objc_allocateClassPair(Class superclass, const char * name, size_t extraBytes)
//注册Class
   void objc_registerClassPair(Class cls)
//添加变量
   class_addIvar(Class cls, const char * name,size_t size, uint8_t alignment , const char * types)
//添加方法
   class_addMethod(Class cls, SEL name, IMP  imp, const char * types)
//获取属性
   class_getProperty(Class  cls, const char * name)
//获取实例变量
   class_getInstanceVariable(Class cls, const char * name)

代码实现

1、工程中新建三个控制器,命名为
RLGongZhongHaoViewController
RLWechatViewController
RLQQQunViewController
RLJianShuViewController

然后在ViewController模拟根据不同数据跳转不同界面,代码如下

#import "ViewController.h"
#import <objc/message.h>

@interface ViewController ()

@property (weak, nonatomic) IBOutlet UISegmentedControl *segment;
@property (nonatomic, copy) NSString *classStr;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.segment.selectedSegmentIndex = 0;
    
}


- (IBAction)selectAnyOne:(id)sender {
//  switch (self.segment.selectedSegmentIndex) {
//        case 0:
//            _classStr = @"RLWechatViewController";
//            break;
//        case 1:
//            _classStr = @"RLQQQunViewController" ;
//
//            break;
//        case 2:
//            _classStr = @"RLGongZhongHaoViewController" ;
//
//            break;
//        case 3:
//            _classStr = @"RLJianShuViewController" ;
//
//            break;
//
//        default:
//            break;
//    }
//
 }


- (IBAction)pushGo:(id)sender {
    [self pushToAnyWhere:_classStr];

}

- (void)pushToAnyWhere:(NSString *)class{
    //将String转化成class类型
    const char * className = [class UTF8String];
    Class cls = objc_getClass(className);

   
    //2.创建实例对象,给属性赋值
    id instance = [[cls alloc]init];
    
    //2.跳转到对应的界面
    [self.navigationController pushViewController:instance animated:YES];
    
    
}


- (IBAction)pushGoWithData:(id)sender {
    NSDictionary * infoDic = nil;
    
    switch (self.segment.selectedSegmentIndex) {
        case 0:
            infoDic = @{@"class":@"RLWechatViewController",
                        @"property":@{
                                @"wechat":@"lei290138645"
                                }
                        };
            break;
        case 1:
            infoDic = @{@"class":@"RLQQQunViewController",
                        @"property":@{
                                @"qq":@"290138645"
                                }
                        };
            break;
        case 2:
            infoDic = @{@"class":@"RLGongZhongHaoViewController",
                        @"property":@{
                                @"gongzhonghao":@"iOS每日推"
                                }
                        };
            break;
        case 3:
            //NewViewController
            infoDic = @{@"class":@"RLJianShuViewController",
                        @"property":@{
                                @"jianshu":@"丨Majestic灬磊"
                                }
                        };
            break;
        case 4:
            //NewViewController
            infoDic = @{@"class":@"newViewController",
                        @"property":@{
                                @"new":@"公众号名称:iOS每日推",
                                @"des":@"本页面运行时创建"
                                }
                        };
            break;
            
        default:
            break;
    }
    
    [self pushToControllerWithData:infoDic];
}

-(void)pushToControllerWithData:(NSDictionary * )vcData{
    //1.获取class
    const char * className = [vcData[@"class"] UTF8String];
    Class cls = objc_getClass(className);
    if(!cls){
        //创建新的类,并添加变量和方法
        Class superClass = [UIViewController class];
        cls = objc_allocateClassPair(superClass, className, 0);
        //添加phoneNumber变量
 
        class_addIvar(cls, "new", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        class_addIvar(cls, "des", sizeof(NSString *), log2(sizeof(NSString *)), @encode(NSString *));
        //添加titleLab控件
        class_addIvar(cls, "titleLab", sizeof(UILabel *), log2(sizeof(UILabel *)), @encode(UILabel *));
        //添加方法,方法交换,执行viewDidLoad加载
        Method method = class_getInstanceMethod([self class], @selector(newLoad));
        IMP methodIMP = method_getImplementation(method);
        const char * types = method_getTypeEncoding(method);
        class_addMethod(cls, @selector(viewDidLoad), methodIMP, types);
    }
    //2.创建实例对象,给属性赋值
    id instance = [[cls alloc]init];
    NSDictionary * values = vcData[@"property"];
    [values enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
        //检测是否存在为key的属性
        if(class_getProperty(cls, [key UTF8String])){
            [instance setValue:obj forKey:key];
        }
        //检测是否存在为key的变量
        else if (class_getInstanceVariable(cls, [key UTF8String])){
            [instance setValue:obj forKey:key];
        }
    }];
    
    //2.跳转到对应的界面
    [self.navigationController pushViewController:instance animated:YES];
    
}

-(void)newLoad{
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blackColor];
    //初始化titleLab
    [self setValue:[[UILabel alloc]initWithFrame:CGRectMake(0, 100, [[UIScreen mainScreen] bounds].size.width, 50)] forKey:@"titleLab"];
    UILabel * titleLab = [self valueForKey:@"titleLab"];
    titleLab.numberOfLines = 0;
    titleLab.textAlignment = NSTextAlignmentCenter;
    //添加到视图上
    [[self valueForKey:@"view"] performSelector:@selector(addSubview:) withObject:titleLab];
    titleLab.text = [NSString stringWithFormat:@"%@\n%@", [self valueForKey:@"new"],[self valueForKey:@"des"]];

    titleLab.textColor = [UIColor whiteColor];
    
}

@end
 

扫描下方公众号👇回复【runtime】获取源码


公众号扫描二维码.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容