iOS热修复之LYFix(基于aspects)
点击上方“iOS开发”,选择“置顶公众号”
关键时刻,第一时间送达!
作者:iOS开发Go
链接:https://www.jianshu.com/p/2fd3018955e2
iOS开发整理发布,转载请联系作者获得授权
简介
LYFix Demo
LYFix 基于 Aspects (做了一点改动),NSInvocation实现的简单hotfix方案。
LYFix 通过下发js代码提供以下功能:
在任意方法前后加入代码 (基于aspects)。
替换任意方法的实现 (基于aspects)。
调用任意方法 (基于NSInvocation)。
修改方法参数 (基于NSInvocation)。
修改方法返回值 (基于NSInvocation)。
使用
1. 初始化
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
[LYFix Fix];
return YES;
}
使用的aspects的枚举
typedef NS_OPTIONS(NSUInteger, AspectOptions) {
AspectPositionAfter = 0, /// Called after the original implementation (default)
AspectPositionInstead = 1, /// Will replace the original implementation.
AspectPositionBefore = 2, /// Called before the original implementation.
AspectOptionAutomaticRemoval = 1 < < 3 /// Will remove the hook after the first execution.
};
2.方法前插入代码
fixMethod('Test', 'runBeforeInstanceMethod', 2,
function(){
runMethod('Test', 'log');
});
3.方法后插入代码
fixMethod('Test', 'runAfterInstanceMethod', 0,
function(){
runMethod('Test', 'log');
});
4.替换方法
fixMethod('Test', 'runInsteadClassMethod', 1,
function(){
runMethod('Test', 'log');
});
5.修复方法
fixMethod('Test', 'instanceMethodCrash:', 1,
function(instance, originInvocation, originArguments) {
if (originArguments[0] == null) {
runError('Test', 'instanceMethodCrash');
} else {
runInvocation(originInvocation);
}
});
6.修改参数
fixMethod('Test','runWithParams:',1,function(instance,invocation,arg){
var array = new Array('123');
var arr = new Array(array);
setInvocationArguments(invocation,arr);
runInvocation(invocation);
});
7.修改返回值
fixMethod("Test", "changeReturnValue:", 1, function(instance, invocation, arg) {
runInvocation(invocation);
setInvocationReturnValue(invocation,'new returnValue');
});
源码
初始化方法,有其他方法供js调用还可以在此继续添加。
+ (void)Fix {
JSContext *context = [self context];
context[@"fixMethod"] = ^(NSString *className, NSString *selectorName, AspectOptions options, JSValue *fixImp) {
[self fixWithClassName:className opthios:options selector:selectorName fixImp:fixImp];
};
context[@"runMethod"] = ^id(NSString * className, NSString *selectorName, id arguments) {
id obj = [self runWithClassname:className selector:selectorName arguments:arguments];
return obj;
};
context[@"runInstanceMethod"] = ^id(id instance, NSString *selectorName, id arguments) {
id obj = [self runWithInstance:instance selector:selectorName arguments:arguments];
return obj;
};
context[@"runClassMethod"] = ^id(NSString * className, NSString *selectorName, id arguments) {
id obj = [self runWithClassname:className selector:selectorName arguments:arguments];
return obj;
};
context[@"runInvocation"] = ^(NSInvocation *invocation) {
[invocation invoke];
};
context[@"setInvocationArguments"] = ^(NSInvocation *invocation, id arguments) {
if ([arguments isKindOfClass:NSArray.class]) {
invocation.arguments = arguments;
} else {
[invocation setMyArgument:arguments atIndex:0];
}
};
context[@"setInvocationArgumentAtIndex"] = ^(NSInvocation *invocation, id argument,NSInteger index) {
[invocation setMyArgument:argument atIndex:index];
};
context[@"setInvocationReturnValue"] = ^(NSInvocation *invocation, id returnValue) {
invocation.returnValue_obj = returnValue;
};
context[@"runError"] = ^(NSString *instanceName, NSString *selectorName) {
NSLog(@"runError: instanceName = %@, selectorName = %@", instanceName, selectorName);
};
context[@"runLog"] = ^(id logs) {
NSLog(@"xly--%@",logs);
};
}
基于aspects前插、后插、替换
+ (void)fixWithClassName:(NSString *)className opthios:(AspectOptions)options selector:(NSString *)selector fixImp:(JSValue *)fixImp {
Class cla = NSClassFromString(className);
SEL sel = NSSelectorFromString(selector);
if ([cla instancesRespondToSelector:sel]) {
} else if ([cla respondsToSelector:sel]){
cla = object_getClass(cla);
} else {
return;
}
[cla aspect_hookSelector:sel withOptions:options usingBlock:^(id aspectInfo) {
[fixImp callWithArguments:@[aspectInfo.instance, aspectInfo.originalInvocation, aspectInfo.arguments]];
} error:nil];
}
基于NSInvocation方法调用
+ (id)runWithInstance:(id)instance selector:(NSString *)selector arguments:(NSArray *)arguments {
if (!instance) {
return nil;
}
SEL sel = NSSelectorFromString(selector);
NSLog(@"xly--%@",[instance class]);
if ([instance isKindOfClass:JSValue.class]) {
instance = [instance toObject];
}
NSMethodSignature *signature = [instance methodSignatureForSelector:sel];
if (!signature) {
return nil;
}
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = sel;
invocation.arguments = arguments;
[invocation invokeWithTarget:instance];
return invocation.returnValue_obj;
}
+ (id)runWithClassname:(NSString *)className selector:(NSString *)selector arguments:(NSArray *)arguments {
Class cla = NSClassFromString(className);
SEL sel = NSSelectorFromString(selector);
if ([cla instancesRespondToSelector:sel]) {
id instance = [[cla alloc] init];
NSMethodSignature *signature = [instance methodSignatureForSelector:sel];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = sel;
invocation.arguments = arguments;
[invocation invokeWithTarget:instance];
return invocation.returnValue_obj;
} else if ([cla respondsToSelector:sel]){
NSMethodSignature *signature = [cla methodSignatureForSelector:sel];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
invocation.selector = sel;
invocation.arguments = arguments;
[invocation invokeWithTarget:cla];
return invocation.returnValue_obj;
} else {
return nil;
}
}
NSInvocation拓展(提供修改参数,返回值等)
▼点击「阅读原文」进入程序员商城
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
关注网络尖刀微信公众号
随时掌握互联网精彩
随时掌握互联网精彩
赞助链接
排名
热点
搜索指数
- 1 习近平G20里约峰会展现大国担当 7979380
- 2 多国驻乌克兰大使馆因袭击风险关闭 7920724
- 3 78岁老太将减持2.5亿股股票 7889753
- 4 二十国集团里约峰会将会卓有成效 7740490
- 5 俄导弹击中乌水电站大坝 7691804
- 6 孙颖莎王艺迪不敌日本削球组合 7536566
- 7 高三女生酒后被强奸致死?检方回应 7484049
- 8 第一视角记录虎鲨吞下手机全程 7353531
- 9 73岁王石独自带娃被偶遇 7257068
- 10 智慧乌镇点亮数字经济新未来 7174242