2012-05-27 9 views
7

Al comunicarse entre JavaScript en una instancia de WebView y WebViewDelegate, los tipos de JavaScript y Objective-C se convierten de ida y vuelta. Por ejemplo, cuando se llama a una función de Objective-C desde JavaScript, una cadena se convierte en NSString, un número se convierte en NSNumber y un objeto se convierte en WebScriptObject.¿Cómo leer de forma segura las propiedades de un WebScriptObject?

Los otros son bastante simples de manejar, pero WebScriptObject parece extraño.

Al pasar un diccionario como {"foo": 1, "bar": 2}, la mayor parte del código que veo extrae las propiedades utilizando valueForKey, como en [[arg valueForKey:@"foo"] intValue] == 1

pero ¿qué pasa si usted no está seguro de si existe la propiedad? ¿Qué pasa si las claves son opcionales? [arg valueForKey:@"baz"] arroja una excepción.

Una cosa que puedo hacer es algo así como

@try { 
    foo = [[arg valueForKey:@"baz"] intValue]; 
} 
@catch (NSException* e) { 
    foo = 0; 
} 

pero he oído que las excepciones en Objective-C son inseguros y no deben ser utilizados para el control de flujo.

La única manera en que puedo pensar en una cierta variación del método utilizado aquí: http://edotprintstacktrace.blogspot.com/2011/10/sample-webscriptobject-javascript.html

En otras palabras: 1. El uso evaluateWebScript para definir una función de JavaScript que implementa Object.keys 2. llamar a esa función en su WebScriptObject 3. itere a través del conjunto de claves devuelto, y solo llame al valueForKey si encontramos una coincidencia.

Esto me parece increíblemente ineficiente. Debe haber una mejor manera ... ¿hay?

Respuesta

3

Creo que encontré algo que funciona, el truco es que puede convertir WebScriptObject a JSObjectRef con -JSObject, y hay un montón de métodos de C que funcionan en JSObjectRefs, aunque los documentos son un poco insuficientes por lo que es difícil averiguar qué hacer exactamente.

Así es como se puede comprobar si existe una propiedad:

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); 
} 

Si quieres ver todas las propiedades (cuyas claves no se conocen de antemano), tendrá que utilizar unas cuantas más funciones:

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 
} 
+0

¿De dónde saca el array 'properties' de? ¿Y qué es 'opciones'? –

+0

Y qué es esta una subclase de –

+0

Lo siento, eso no estaba claro ... En este caso, 'options' es el' WebScriptObject' del que quiero extraer cosas. 'properties' es una especie de matriz de campos que le interesan, si sabe de antemano qué teclas desea verificar en el diccionario. Si desea leer objetos arbitrarios cuyas claves no conoce de antemano, puede usar 'JSObjectCopyPropertyNames' para obtener la lista de claves de' JSObject' y luego 'JSPropertyNameArrayGetCount' y' JSPropertyNameArrayGetNameAtIndex' para procesar esa matriz. –

4

Desde OS X 10.9 e iOS 7 hay formas mucho más sencillas de hacerlo. Apple presentó una clase llamada JSValue wich se puede utilizar para tender un puente sobre objetos JavaScript a objetos ObjC regulares:

// 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]); 
    } 
} 
Cuestiones relacionadas