如何安全地读取WebScriptObject的属性?

时间:2012-05-27 21:34:46

标签: javascript objective-c webview webkit

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

这对我来说似乎非常低效。必须有更好的方式......有吗?

2 个答案:

答案 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
}