7

WebView实例中的 JavaScript 和 a之间进行通信时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定义实现的 JavaScript 函数Object.keys2. 在您的函数上调用该函数WebScriptObject3. 遍历返回的键数组,并且仅valueForKey在找到匹配项时调用。

对我来说,这似乎非常低效。一定有更好的方法……有吗?

4

2 回答 2

4

从 OS X 10.9 和 iOS 7 开始,有一种更简单的方法可以做到这一点。Apple 引入了一个名为JSValuewich 的类,可用于将 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]);
    }
}
于 2015-01-14T14:37:43.050 回答
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
}
于 2013-12-04T23:56:33.803 回答