Skip to content

defineProtocol 使用文档

bang edited this page Mar 29, 2017 · 5 revisions

问题

JSPatch 为一个类新增原本 OC 不存在的方法时,所有的参数类型都会定义为 id 类型,因为这种在 JS 里新增的方法一般不会在 OC 上调用,而是在 JS 上用,JS 可以认为一切变量都是对象,没有类型之分,所以全部定义为 id 类型。

但有种场景,需要让新增的方法参数类型不是 id,那就是,在 OC 里 .h 文件定义了一个方法,这个方法里的参数和返回值不都是 id 类型,但是在 .m 文件中由于疏忽没有实现这个方法,导致其他地方调用这个方法时找不到这个方法造成 crash,要用 JSPatch 修复这样的 bug,就需要 JSPatch 可以动态添加指定参数类型的方法。对此可以用 defineProtocol() 接口定义参数类型,再在 defineClass() 中实现,就可以动态添加指定参数类型的方法了。

API

defineProtocol(protocolDeclaration, instanceMethods , classMethods)

@param protocolDeclaration:字符串,新增protocol的名字
@param instanceMethods:字典,要添加到protocol的实例方法
@param classMethods:字典,要添加到protocol的类方法

需要说明的是,在runtime中,协议一旦注册后就不可再修改,所以只能新增协议

  1. 在defineProtocol中protocolDeclaration参数输入协议名称
  2. 在defineProtocol中instanceMethods参数输入协议内实例方法的字典,以方法名为Key,Value也是一个字典,是实例方法的信息
  3. 在defineProtocol中classMethods参数输入协议内类方法的字典,以方法名为Key,Value也是一个字典,是类方法的信息

## 示例

例如在 JPTestObject 对象中,.h头文件定义了这样一个接口:

@interface JPTestObject
- (NSString *)stringWithRect:(CGRect)rect withNum:(float)num withArray:(NSArray *)arr;
@end

但在 .m 文件中没有实现这个接口,导致其他地方调用这个方法时 crash。对此可以用 JSPatch 修复,分两个步骤:

  1. 把这个方法定义成一个 protocol
  2. defineClass中实现这个 protocol

先看第一步,用 defineProtocol 接口把这个方法定义在一个新的 protocol 中:

defineProtocol('JPDemoProtocol',{
   stringWithRect_withNum_withArray: {
       paramsType:"CGRect, float, NSArray*",
       returnType:"id",
   },
}

接着第二步,在 defineClass 中实现这个方法:

defineClass('JPTestObject : NSObject <JPDemoProtocol>', {
    stringWithRect_withNum_withArray:function(rect, num, arr){
        //use rect/num/arr params here
        return @"success";
    },
}

大功告成,新增的方法参数类型会遵循 defineProtocol 里定义的类型,这时 OC 中再调用这个方法时就会调到在 JSPatch 新增的方法,让 APP 避免 crash。

更多使用 Case 参见 demo 工程里的 newProtocolTest.js

更多参数类型

1.paramsTypereturnType 中可以直接使用的参数类型位包括id, BOOL, int, void, char, short, unsigned short, unsigned int, long, unsigned long, long long, float, double, CGFloat, CGSize, CGRect, CGPoint, CGSize, CGVector, UIEdgeInset, NSInteger, SEL, block

2.参数是 OC 对象 NSObject,比如NSArrayCustomObject,可以写作id,也可以直接写类名,效果一样 paramsType:"id" paramsType:"CustomObject"

3.对于无法支持的参数,比如其他结构体,customStruct,可以使用typeEncode可选字段

testProtocolConstumStruct:{
   paramsType:"unknown",
   returnType:"int",
   typeEncode:"i@:{CGVector=dd}"
}, 	       

4.当使用 typeEncode 时,类型字符串可以不必匹配任意填写,比如填写unknown,比如填写xxx,但是要求paramsType的个数必须保证准确

5.自行填写的 typeEncode,可以通过在项目中使用OC代码,在运行时通过系统获取

SEL selstr = NSSelectorFromString(@"testProtocolConstumStruct:");
Method method = class_getInstanceMethod(class, selstr);
const char* type = method_getTypeEncoding(logmanagermethod);
Clone this wiki locally