2012-05-27 3 views
7

Quando si comunica tra JavaScript in un'istanza WebView e uno WebViewDelegate, i tipi JavaScript e i tipi Objective-C vengono convertiti avanti e indietro. Ad esempio, quando si chiama una funzione Objective-C da JavaScript, una stringa diventa NSString, un numero diventa NSNumber e un oggetto diventa WebScriptObject.Come leggere in modo sicuro le proprietà di un oggetto WebScriptObject?

Gli altri sono abbastanza semplici da gestire, ma lo WebScriptObject sembra strano.

Quando si passa un dizionario come {"foo": 1, "bar": 2}, la maggior parte del codice vedo estratti le proprietà utilizzando valueForKey, come ad esempio in [[arg valueForKey:@"foo"] intValue] == 1

ma che dire se non siete sicuri se la proprietà esiste? Cosa succede se i tasti sono opzionali? [arg valueForKey:@"baz"] genera un'eccezione.

Una cosa che posso fare è qualcosa di simile

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

ma ho sentito che le eccezioni in Objective-C non sono sicuri e non devono essere utilizzati per il controllo di flusso.

L'unico altro modo mi viene in mente è una certa variante del metodo utilizzato qui: http://edotprintstacktrace.blogspot.com/2011/10/sample-webscriptobject-javascript.html

In altre parole: 1. utilizzo evaluateWebScript per definire una funzione JavaScript che implementa Object.keys 2. chiamare tale funzione sul vostro WebScriptObject 3. scorrere l'array restituito di chiavi e chiamare solo valueForKey se troviamo una corrispondenza.

Questo mi sembra incredibilmente inefficiente. Ci deve essere un modo migliore ... c'è?

risposta

3

Penso di aver trovato qualcosa che funzioni - il trucco è che puoi convertire WebScriptObject in JSObjectRef con -JSObject, e c'è un sacco di metodi C che funzionano su JSObjectRefs, anche se i documenti sono un po 'carenti quindi è difficile capire cosa fare esattamente.

Ecco come si può verificare se una proprietà esiste:

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

Se si desidera elencare tutte le proprietà (le cui chiavi non sono noti in anticipo), è necessario utilizzare un paio di più funzioni:

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

Da dove prendi l'array 'properties' da? E quali sono le "opzioni"? –

+0

E cos'è questa sottoclasse di –

+0

Scusa, non era chiaro ... In questo caso, 'options' è il' WebScriptObject' di cui voglio estrarre le cose. 'properties' è una sorta di matrice di campi a cui sei interessato, se conosci in anticipo quali tasti vuoi controllare nel dizionario. Se si desidera leggere oggetti arbitrari le cui chiavi non si conoscono in anticipo, è possibile utilizzare 'JSObjectCopyPropertyNames' per ottenere l'elenco di chiavi da' JSObject' e quindi 'JSPropertyNameArrayGetCount' e' JSPropertyNameArrayGetNameAtIndex' per elaborare tale array. –

4

Da OS X 10.9 e iOS 7 c'è un modo più semplice per farlo. Apple ha introdotto una classe denominata JSValue Quale può essere utilizzato per colmare oggetti JavaScript agli oggetti objc regolari:

// 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]); 
    } 
} 
Problemi correlati