2018年9月21日
一.前期准备
1.如需要项目支持objc_msgSend
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.如果不用函数原型强转,真机运行会奔溃,模拟器是正常的,
现象:
原因:指令集不同,必须定义原型才可以使用。
修改成如下即可:
id classAlloc = ((id (*) (id, SEL))objc_msgSend)(classCon, sel_registerName("alloc"));
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];
如果您发现本文对你有所帮助,如果您认为其他人也可能受益,请把它分享出去。