在WebView
实例中的JavaScript与WebViewDelegate
之间进行通信时,JavaScript类型和Objective-C类型会来回转换。例如,当从JavaScript调用Objective-C函数时,字符串变为NSString
,数字变为NSNumber
,对象变为WebScriptObject
。
其他的处理非常简单,但WebScriptObject
似乎很奇怪。
传递{"foo": 1, "bar": 2}
之类的字典时,我看到的大部分代码都会使用valueForKey
提取属性,例如[[arg valueForKey:@"foo"] intValue] == 1
但是如果你不确定该物业是否存在呢?如果键是可选的怎么办? [arg valueForKey:@"baz"]
会抛出异常。
我可以做的一件事就是
@try {
foo = [[arg valueForKey:@"baz"] intValue];
}
@catch (NSException* e) {
foo = 0;
}
但我听说Objective-C中的异常是不安全的,不应该用于流量控制。
我能想到的另一种方法是使用此方法的一些变体:http://edotprintstacktrace.blogspot.com/2011/10/sample-webscriptobject-javascript.html
换句话说:1。使用evaluateWebScript
定义实现Object.keys
的JavaScript函数2.在WebScriptObject
上调用该函数3.迭代返回的键数组,并且如果我们找到匹配项,只能致电valueForKey
。
这对我来说似乎非常低效。必须有更好的方式......有吗?
答案 0 :(得分:4)
从OS X 10.9和iOS 7开始,有一种更简单的方法可以做到这一点。 Apple引入了一个名为JSValue
的类,它可以用来将JavaScript对象桥接到常规的ObjC对象:
// WebScriptObject *options; obtained from a callback in ObjC
id objCObject = [[options JSValue] toObject];
if ([objCObject isKindOfClass:[NSArray class]]) {
for (id object in objCObject) {
NSLog(@"object: %@", object);
}
}
else if ([objCObject isKindOfClass:[NSDictionary class]]) {
for (id<NSCopying> key in [objCObj allKeys]) {
NSLog(@"object for key %@: %@", key, [objCObject objectForKey:key]);
}
}
答案 1 :(得分:3)
我想我发现了一些有用的东西 - 诀窍是你可以用-JSObject将WebScriptObject转换为JSObjectRef,并且有一大堆C方法可以在JSObjectRefs上工作,虽然文档有点缺乏所以很难计算该怎么做。
以下是检查属性是否存在的方法:
id getProperty(WebScriptObject *obj, NSString *prop) {
JSStringRef jsProp = JSStringCreateWithCFString((__bridge CFStringRef) prop);
if (JSObjectHasProperty(self.frame.globalContext, [obj JSObject], jsProp)) {
return [options valueForKey:prop];
} else {
return nil;
}
JSStringRelease(jsString);
}
如果要列出所有属性(其密钥未提前知道),则需要使用更多功能:
JSPropertyNameArrayRef properties =
JSObjectCopyPropertyNames(self.frame.context, [obj JSObject]);
size_t count = JSPropertyNameArrayGetCount(properties);
for (NSInteger i = 0; i < count; i++) {
JSStringRef property = JSPropertyNameArrayGetNameAtIndex(properties, i);
// ... etc. as above
}