用运行时命令实现不引入头文件

2018年9月21日
一.前期准备
1.如需要项目支持objc_msgSend

工程如下修改:
image.png

2.常见几种类型 函数原型
a.无返回值  无参数
void * (*action)(id, SEL) = (void *(*)(id, SEL)) objc_msgSend;

b.无返回值  带一个参数(多个参数类推即可)
 void * (*action)(id, SEL,NSString*) = (void *(*)(id, SEL,NSString*)) objc_msgSend;
 void * (*action)(id, SEL,id) = (void *(*)(id, SEL,id)) objc_msgSend;

c.返回值  带一个参数
NSString * (*action)(id, SEL,NSString*) = (NSString *(*)(id, SEL,NSString*)) objc_msgSend;
id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
id (*action)(id, SEL, NSInteger) = (id(*)(id, SEL, NSInteger)) objc_msgSend;

2.1.如果不用函数原型强转,真机运行会奔溃,模拟器是正常的,
现象:


image.png

原因:指令集不同,必须定义原型才可以使用。
修改成如下即可:

id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
image.png

3.如何获取页面里属性声明的类型

//获取属性类型
+(NSString *)getPropertyType:(NSString *)property inCon:(id)toCon{
    //获取对象的类型objc_getClass("UserModel")
    objc_property_t p = class_getProperty([toCon class], property.UTF8String);
//    const char *name = property_getAttributes(p);
//    NSLog(@"%s==",name);
    // 2.成员类型
    NSString *attrs = @(property_getAttributes(p));
    NSUInteger dotLoc = [attrs rangeOfString:@","].location;
    NSString *code = nil;
    NSUInteger loc = 3;
    if (dotLoc == NSNotFound) { //
        code = [attrs substringFromIndex:loc];
    } else {
        code = [attrs substringWithRange:NSMakeRange(loc, dotLoc - loc-1)];
    }
//    NSLog(@"%@===%@====",code,attrs);
    return code;
}


//使用
NSString *key =  @“type”;
Class classCon = NSClassFromString(className);
id =  toCon= [[classCon alloc] init];
NSString *objType = [HuControllerId getPropertyType:key inCon:toCon];
                                if ([objType hasSuffix:@"NSString"]) {
                                    id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), paramOne);
                                }else if([objType hasSuffix:@"CGFloat"]){
                                    CGFloat vale = [val doubleValue];
                                    id (*action)(id, SEL, CGFloat) = (id(*)(id, SEL, CGFloat)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), vale);
                                }

3.1如果类型不匹配程序就会奔溃
如果value是Number,但是类型定义的是NSString,如下调用就会奔溃

 if ([value isKindOfClass:[NSNumber class]]) {
                NSInteger val = [(NSNumber *) value integerValue];
                void (*action)(id, SEL, NSInteger) = (void (*)(id, SEL, NSInteger)) objc_msgSend;
                action(toCon, method, val);
            }

修改成如下即可

            //等价于controller.shuxing = value;
            if ([value isKindOfClass:[NSNumber class]] && ![[HuControllerId getPropertyType:key inCon:toCon] isEqualToString:@"NSString"]) {
                NSInteger val = [(NSNumber *) value integerValue];
                void (*action)(id, SEL, NSInteger) = (void (*)(id, SEL, NSInteger)) objc_msgSend;
                action(toCon, method, val);
            } else {
                void (*action)(id, SEL, id) = (void (*)(id, SEL, id)) objc_msgSend;
                action(toCon, method, value);
            }

4.用如上运行时命令 实现不需要引入头文件的页面获取

#import 
@interface HuControllerId : NSObject
+ (HuControllerId *)HuControllerShare;
/**
 指定页面跳转 (不含埋点数据)
 
 @param fromCon 指定页面
 @param conName 页面类
 @param type 页面类型
 @param paramDic 相关参数
 */
- (void)pushFromController:(UIViewController *)fromCon toConName:(NSString *)conName paramType:(HuPushControllerType)type param:(NSDictionary *)paramDic;
/**
 获取指定名字的类对象
 
 @param conName 类名
 @param type 页面类型
 @param paramDic 相关参数
 @return 类对象
 */
- (UIViewController *)getViewControllerWithConName:(NSString *)conName paramType:(HuPushControllerType)type param:(NSDictionary *)paramDic;
@end


#import "HuControllerId.h"
#import 
@interface HuControllerId ()
@end
@implementation HuControllerId
static HuControllerId *instence = nil;
+ (HuControllerId *)HuControllerShare {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instence = [[HuControllerId alloc] init];
    });
    return instence;
}

- (void)pushFromController:(UIViewController *)fromCon toConName:(NSString *)conName paramType:(HuPushControllerType)type param:(NSDictionary *)paramDic{
    if (!conName || conName.length == 0) {
        return;
    }
    //根据定义好的pageId,拿到类名的字符串
    NSString *className = conName;
    //根据类名转化为Class类型
    Class classCon = NSClassFromString(className);
    //初始化并分配内存
    id toCon;
    //根据HuPushControllerType判断是否有值传到下一页
    switch (type) {
        case HuPushNoParam: {
            toCon= [[classCon alloc] init];
        } break;
        case HuPushProperty: {
            toCon= [[classCon alloc] init];
            [self getToConFromProperty:paramDic toCon:toCon];
        } break;
        case HuPushInit: {
            toCon = [self getToConFromInit:paramDic classCon:classCon];
        } break;
        case HuPushOther: {
            toCon = [self getToConFromInit:paramDic classCon:classCon];
            [self getToConFromProperty:paramDic toCon:toCon];
        } break;
        default:
            break;
    }
    if (toCon) {
        [fromCon.navigationController pushViewController:toCon animated:YES];
    }
}
- (UIViewController *)getViewControllerWithConName:(NSString *)conName paramType:(HuPushControllerType)type param:(NSDictionary *)paramDic{
    if (!conName || conName.length == 0) {
        return nil;
    }
    
    //根据定义好的pageId,拿到类名的字符串
    NSString *className = conName;
    //根据类名转化为Class类型
    Class classCon = NSClassFromString(className);
    //初始化并分配内存
    id toCon;
    //根据HuPushControllerType判断是否有值传到下一页
    switch (type) {
        case HuPushNoParam: {
            toCon= [[classCon alloc] init];
        } break;
        case HuPushProperty: {
            toCon= [[classCon alloc] init];
            [self getToConFromProperty:paramDic toCon:toCon];
        } break;
        case HuPushInit: {
            toCon = [self getToConFromInit:paramDic classCon:classCon];
        } break;
        case HuPushOther: {
            toCon = [self getToConFromInit:paramDic classCon:classCon];
            [self getToConFromProperty:paramDic toCon:toCon];
        } break;
        default:
            break;
    }
    if (toCon) {
        return toCon;
    }
    return  nil;
}
- (id)getToConFromInit:(NSDictionary *)paramDic classCon:(Class)classCon {
    id toCon = nil;
    NSDictionary *initDic = [paramDic valueForKey:HuInitWith];
    NSString *key = [initDic allKeys].firstObject;
    //把OC的字符串改成C语言的字符串
    const char *ky = [key UTF8String];
    NSArray *value = [initDic valueForKey:key];
    //这里判断value数组元素个数是否和key按:分割成数组后的个数相等
    if ([key containsString:@":"] && value) {
        if ([key componentsSeparatedByString:@":"].count == (value.count + 1)) {
            switch (value.count) {
                    case 1: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id paramOne =  [value objectAtIndex:0];
                            if ([paramOne isKindOfClass:[NSNumber class]]) {
                                NSString *val = [(NSNumber *) paramOne stringValue];
                                NSString *objType = [HuControllerId getPropertyType:key inCon:classAlloc];
                                if ([objType hasSuffix:@"NSString"]) {
                                    id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), paramOne);
                                }else if([objType hasSuffix:@"CGFloat"]){
                                    CGFloat vale = [val doubleValue];
                                    id (*action)(id, SEL, CGFloat) = (id(*)(id, SEL, CGFloat)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), vale);
                                }else{
                                    id (*action)(id, SEL, NSInteger) = (id(*)(id, SEL, NSInteger)) objc_msgSend;
                                    //等价于[[class alloc] iniWith:]
                                    toCon = action(classAlloc, sel_registerName(ky), [val integerValue]);
                                }
                                
                            }else{
                                id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
                                toCon = action(classAlloc, sel_registerName(ky), paramOne);
                            }
                            
                        }
                    } break;
                    case 2: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id paramOne = [value objectAtIndex:0];
                            id paramTwo = [value objectAtIndex:1];
                            
                            if ([paramTwo isKindOfClass:[NSNumber class]]) {
                                id (*action)(id, SEL, id, NSInteger) = (id(*)(id, SEL, id, NSInteger)) objc_msgSend;
                                toCon = action(classAlloc, sel_registerName(ky), paramOne, [paramTwo integerValue]);
                            }else{
                                id (*action)(id, SEL, id, id) = (id(*)(id, SEL, id, id)) objc_msgSend;
                                toCon = action(classAlloc, sel_registerName(ky), paramOne, paramTwo);
                            }
    
                        }
                    } break;
                    case 3: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id paramTwo = [value objectAtIndex:1];
                            id paramThree = [value objectAtIndex:2];
                            if ([paramTwo isKindOfClass:[NSNumber class]]) {
                                if ([paramThree isKindOfClass:[NSNumber class]]) {
                                    id (*action)(id, SEL, id, NSInteger, NSInteger) = (id(*)(id, SEL, id, NSInteger, NSInteger)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [[value objectAtIndex:1] integerValue], [[value objectAtIndex:2] integerValue]);
                                }else{
                                    id (*action)(id, SEL, id, NSInteger, id) = (id(*)(id, SEL, id, NSInteger, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [[value objectAtIndex:1] integerValue], [value objectAtIndex:2]);
                                }
                                
                            }else{
                                if ([paramThree isKindOfClass:[NSNumber class]]) {
                                    id (*action)(id, SEL, id, id, NSInteger) = (id(*)(id, SEL, id, id, NSInteger)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [[value objectAtIndex:2] integerValue]);
                                }else{
                                    id (*action)(id, SEL, id, id, id) = (id(*)(id, SEL, id, id, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [value objectAtIndex:2]);
                                }
                            }
                        }
                    } break;
                    case 4: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id (*action)(id, SEL, id, id, id, id) = (id(*)(id, SEL, id, id, id, id)) objc_msgSend;
                            toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [value objectAtIndex:2], [value objectAtIndex:3]);
                        }
                    } break;
                default:
                    break;
            }
        }
    }
    return toCon;
}

- (void)getToConFromProperty:(NSDictionary *)paramDic toCon:(id)toCon {
    //需要属性传值,则通过运行时来解决
    if (!toCon) {
        return;
    }
    NSDictionary *propertyDic = [paramDic valueForKey:HuProperty];
    NSArray *keyArr = [propertyDic allKeys];
    for (int i = 0; i < keyArr.count; i++) {
        NSString *key = [keyArr objectAtIndex:i];
        id value = [propertyDic valueForKey:key];
        //把key的首字母大写
        NSString *firstStr = [key substringWithRange:NSMakeRange(0, 1)].uppercaseString;
        NSString *restStr = [key substringFromIndex:1];
        //生成对应属性的set方法
        NSString *selName = [NSString stringWithFormat:@"set%@%@:", firstStr, restStr];
        SEL method = NSSelectorFromString(selName);
        if ([toCon respondsToSelector:method]) {
            //等价于controller.shuxing = value;
            if ([value isKindOfClass:[NSNumber class]] && ![[HuControllerId getPropertyType:key inCon:toCon] isEqualToString:@"NSString"]) {
                NSInteger val = [(NSNumber *) value integerValue];
                void (*action)(id, SEL, NSInteger) = (void (*)(id, SEL, NSInteger)) objc_msgSend;
                action(toCon, method, val);
            } else {
                void (*action)(id, SEL, id) = (void (*)(id, SEL, id)) objc_msgSend;
                action(toCon, method, value);
            }
        }
    }
}

- (id)getToConFromInit:(NSDictionary *)paramDic classCon:(Class)classCon {
    id toCon = nil;
    NSDictionary *initDic = [paramDic valueForKey:HuInitWith];
    NSString *key = [initDic allKeys].firstObject;
    //把OC的字符串改成C语言的字符串
    const char *ky = [key UTF8String];
    NSArray *value = [initDic valueForKey:key];
    //这里判断value数组元素个数是否和key按:分割成数组后的个数相等
    if ([key containsString:@":"] && value) {
        if ([key componentsSeparatedByString:@":"].count == (value.count + 1)) {
            switch (value.count) {
                    case 1: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id paramOne =  [value objectAtIndex:0];
                            if ([paramOne isKindOfClass:[NSNumber class]]) {
                                NSString *val = [(NSNumber *) paramOne stringValue];
                                NSString *objType = [HuControllerId getPropertyType:key inCon:classAlloc];
                                if ([objType hasSuffix:@"NSString"]) {
                                    id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), paramOne);
                                }else if([objType hasSuffix:@"CGFloat"]){
                                    CGFloat vale = [val doubleValue];
                                    id (*action)(id, SEL, CGFloat) = (id(*)(id, SEL, CGFloat)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), vale);
                                }else{
                                    id (*action)(id, SEL, NSInteger) = (id(*)(id, SEL, NSInteger)) objc_msgSend;
                                    //等价于[[class alloc] iniWith:]
                                    toCon = action(classAlloc, sel_registerName(ky), [val integerValue]);
                                }
                                
                            }else{
                                id (*action)(id, SEL, id) = (id(*)(id, SEL, id)) objc_msgSend;
                                toCon = action(classAlloc, sel_registerName(ky), paramOne);
                            }
                            
                        }
                    } break;
                    case 2: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id paramOne = [value objectAtIndex:0];
                            id paramTwo = [value objectAtIndex:1];
                            
                            if ([paramTwo isKindOfClass:[NSNumber class]]) {
                                id (*action)(id, SEL, id, NSInteger) = (id(*)(id, SEL, id, NSInteger)) objc_msgSend;
                                toCon = action(classAlloc, sel_registerName(ky), paramOne, [paramTwo integerValue]);
                            }else{
                                id (*action)(id, SEL, id, id) = (id(*)(id, SEL, id, id)) objc_msgSend;
                                toCon = action(classAlloc, sel_registerName(ky), paramOne, paramTwo);
                            }
    
                        }
                    } break;
                    case 3: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id paramTwo = [value objectAtIndex:1];
                            id paramThree = [value objectAtIndex:2];
                            if ([paramTwo isKindOfClass:[NSNumber class]]) {
                                if ([paramThree isKindOfClass:[NSNumber class]]) {
                                    id (*action)(id, SEL, id, NSInteger, NSInteger) = (id(*)(id, SEL, id, NSInteger, NSInteger)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [[value objectAtIndex:1] integerValue], [[value objectAtIndex:2] integerValue]);
                                }else{
                                    id (*action)(id, SEL, id, NSInteger, id) = (id(*)(id, SEL, id, NSInteger, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [[value objectAtIndex:1] integerValue], [value objectAtIndex:2]);
                                }
                                
                            }else{
                                if ([paramThree isKindOfClass:[NSNumber class]]) {
                                    id (*action)(id, SEL, id, id, NSInteger) = (id(*)(id, SEL, id, id, NSInteger)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [[value objectAtIndex:2] integerValue]);
                                }else{
                                    id (*action)(id, SEL, id, id, id) = (id(*)(id, SEL, id, id, id)) objc_msgSend;
                                    toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [value objectAtIndex:2]);
                                }
                            }
                        }
                    } break;
                    case 4: {
                        id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
                        if ([classAlloc respondsToSelector:sel_registerName(ky)]) {
                            id (*action)(id, SEL, id, id, id, id) = (id(*)(id, SEL, id, id, id, id)) objc_msgSend;
                            toCon = action(classAlloc, sel_registerName(ky), [value objectAtIndex:0], [value objectAtIndex:1], [value objectAtIndex:2], [value objectAtIndex:3]);
                        }
                    } break;
                default:
                    break;
            }
        }
    }
    return toCon;
}

@end

4.1 以下常见的几种使用方法
a.HuPushNoParam

UIViewController *vc = [[HuControllerId HuControllerShare] getViewControllerWithConName:@“HuWorkerSatisfactionViewController" paramType:HuPushNoParam param:nil];
[nav pushViewController:vc animated:YES];

原来需要引入头文件

#import “HuWorkerSatisfactionViewController.h"
 UINavigationController *nav = [[_customVc childViewControllers] objectAtIndex:3];
                    HuWorkerSatisfactionViewController *workerVc = [[HuWorkerSatisfactionViewController alloc] init];
                    [nav pushViewController:workerVc animated:YES];

b.HuPushProperty

NSMutableDictionary *param = @{}.mutableCopy;
    param[@"courseId"] = model.courseId;
    param[@"releaseId"] = model.releaseId;
    param[@"listModel"] = model;
    param[@"byPublicCourseInto"] = @(model.type == 2);
    param[@"needAddMovieTimer"] = @(model.type == 2);
    param[@"byElectiveCoursesInto"] = @(model.type == 3);
    param[@"noDeadlineCourse"] = @(NO);
    param[@"fromMainTrainType"] = @(model.standardLiveTrain);
    //规培不需要累计时间
    if (model.type == 4) {
        param[@"needAddMovieTimer"] = @(NO);
    }else if(model.type == 2){
        param[@"needAddMovieTimer"] = @(YES);
    }
    
    //设置时间是否显示
    if (model.type == 3 && model.trainModel == 0) {
        param[@"noDeadlineCourse"] = @(YES);
    }
    
    [[HuControllerId HuControllerShare]pushFromController:vc toConName:@"HuRegulationTrainDetailNewViewController" paramType:HuPushProperty param:@{HuProperty:param}];

原来需要引入头文件

#import "HuRegulationTrainDetailNewViewController.h"   
HuRegulationTrainDetailNewViewController *detail = [HuRegulationTrainDetailNewViewController new];
    detail.courseId = model.courseId;
    detail.releaseId = model.releaseId;
    detail.listModel = model;
    
    detail.byPublicCourseInto = model.type == 2;
    detail.needAddMovieTimer = model.type == 2;
    detail.byElectiveCoursesInto = model.type == 3;
    detail.noDeadlineCourse = NO;
    detail.fromMainTrainType = model.standardLiveTrain;
    //规培不需要累计时间
    if (model.type == 4) {
        detail.needAddMovieTimer = NO;
    }else if(model.type == 2){
        detail.needAddMovieTimer = YES;
    }
    
    //设置时间是否显示
    if (model.type == 3 && model.trainModel == 0) {
        detail.noDeadlineCourse = YES;
    }
    
    [vc.navigationController pushViewController:detail animated:YES];

c.HuPushInit

vc = [[HuControllerId HuControllerShare]getViewControllerWithConName:@"HuMsgViewController" paramType:HuPushInit param:@{HuInitWith:@{@"initWithType:":@(pageType)}}];

原来用法:

#import "HuMsgViewController.h"
vc = [[HuMsgViewController alloc] initWithType:pageType];

d.HuPushOther

        TrainTestListModel *listModel = [[TrainTestListModel alloc]init];
        listModel.releasePaperId = dic[@"data"][@"releasePaperId"] ?: @"";
        listModel.ID = dic[@"data"][@"releaseStudentId"] ?: @"";
        listModel.paperRecordId = dic[@"data"][@"paperRecordId"] ?: @"";
        listModel.canTest = [dic[@"data"][@"canTakeExam"] boolValue] ? @"1" : @"0";
        [[HuControllerId HuControllerShare] pushFromController:weakSelf.currentVC
                                                     toConName:@"TrainTestReportViewController"
                                                     paramType:HuPushOther
                                                         param:@{ HuInitWith : @{@"initWithModel:WithType:" : @[ listModel, @(HuExamTypeExam) ]},
                                                                  HuProperty : @{@"planEndTime" : [HuCommonMethod timeIntervalWithDateStr:model.endTime]} }];

原来用法:

#import "TrainTestReportViewController.h"
                TrainTestListModel *listModel = [[TrainTestListModel alloc] init];
                listModel.releasePaperId = dic[@"data"][@"releasePaperId"] ?: @"";
                listModel.ID = dic[@"data"][@"releaseStudentId"] ?: @"";
                listModel.paperRecordId = dic[@"data"][@"paperRecordId"] ?: @"";
                listModel.canTest = [dic[@"data"][@"canTakeExam"] boolValue] ? @"1" : @"0";
                TrainTestReportViewController *vc = [[TrainTestReportViewController alloc] initWithModel:listModel WithType:HuExamTypeExam];
                vc.planEndTime = [HuConfigration timeIntervalWithDateStr:model.endTime];
                [curVC.navigationController pushViewController:vc animated:YES];

如果您发现本文对你有所帮助,如果您认为其他人也可能受益,请把它分享出去。

你可能感兴趣的:(用运行时命令实现不引入头文件)