这几天遇到一个JSPatch的一个小坑,在OC调用新增方法时出现Crash,但是看Wiki上面的说明,还是有点疑惑,原文是这么写的:"可以给一个类随意添加 OC 未定义的方法,但所有的参数类型都是 id"。
做了一个简单的复现demo,核心代码如下:
- (int)testImp
{
SEL selector = NSSelectorFromString(strMethod);
IMP imp = [self methodForSelector:selector];
int (*func)(id, SEL) = (typeof(func))imp; // void *
int value = func(self, selector);
return value;
}
这个testImp方法是在OC里面的viewDidLoad方法中调用的,而strMethod也是动态赋值的,如果它是新增方法的话,在main.js里增加它的实现,func调用就会出错。
然后我做了另一个测试,在main.js里直接调用这个新增方法,调用与返回值都是正常的。说明只有在OC里调用新增方法才会遇到问题。
本来我以为在OC里调用新增方法出错,是因为在调用它的时候class_addMethod过程还没有发生,于是我在js里把这个新增方法的位置放在viewDidLoad之前,并没有卵用,现在想来太天真。
想过自己要实现class_addMethod,但是这就意味着会与JSPatch自带的class_addMethod冲突,并不靠谱。突然想到之前唯敬之前发的文章JSPatch defineProtocol部分实现详解 - 简书,再仔细一读,发现答案就在这篇文章里面。
与作者交流,发现了另外一种产生此问题的Case:在OC里调用一个有声明无实现的方法,尽管在js里新增它的实现,依然会产生Crash。
我遇到的这个问题与他遇到的问题类似,只不过它的原因是参数为非id类型,我的是返回值为非id类型。通用的原因可以这样描述:希望新增的方法是一个含有非id类型参数或非id类型返回值的方法,而JSPatch最终添加的新方法的参数或返回值都是id,在OC调用时找不到它想要的方法。
解决方法是用文中的defineProtocol来指定新增方法的参数类型与返回值类型,确保生成正确的type encode
。我确定自己以前被标题中的defineProtocol欺骗了,以为只能用于新增协议方法,实际上也可以用于我这种新增方法的需求。只不过因为直接修改defineClass牵涉过大,所以才在defineProtocol中实现。
比如我这种情况就可以这样新增方法:
defineProtocol("testProtocol",{
newMethod:{
returnType:"int",
},
})
有非id类型的入参就这样写:
defineProtocol("testProtocol",{
newMethod:{
paramsType:"BOOL , float , CGFloat",
returnType:"int",
},
})
接着在defineClass中设置遵守testProtocol协议:
defineClass('TestViewController : UIViewController <testProtocol>' , {
})
然后你就可以愉快地在OC中调用这个newMethod了。
网友评论