2009-09-22 6 views
18

Y a-t-il un moyen d'accéder à un élément NSArray avec valueForKeyPath? Le service de géocodage inverse de Google, par exemple, renvoie une structure de données très complexe. Si je veux la ville, en ce moment, je dois le casser en deux appels, comme ceci:Obtention d'éléments de tableau avec valueForKeyPath

NSDictionary *address = [NSString stringWithString:[[[dictionary objectForKey:@"Placemark"] objectAtIndex:0] objectForKey:@"address"]]; 
NSLog(@"%@", [address valueForKeyPath:@"AddressDetails.Country.AdministrativeArea.SubAdministrativeArea.Locality.LocalityName"]); 

me demandais s'il y a un moyen de caser l'appel objectAtIndex: dans la chaîne valueForKeyPath. J'ai essayé une formulation javascript-esque comme @ "Placemark [0] .address" mais pas de dés.

Répondre

17

Malheureusement, non. La documentation complète pour ce qui est autorisé à l'aide du codage de valeur-clé est here. Il n'y a pas, à ma connaissance, d'opérateurs qui vous permettent de saisir un tableau ou un objet particulier.

+0

Vous pouvez récupérer un tableau particulier. Je ne suis pas sûr d'avoir un élément de tableau. Voir http://stackoverflow.com/questions/3165290/how-to-parsing-json-object-in-iphone-sdk-xcode-using-json-framework – Fraggle

+2

Veuillez mettre à jour le lien dans cette réponse, la page n'existe plus . – ZeMoon

+0

Mise à jour du lien. – codehearted

15

Voici une catégorie que je viens d'écrire pour NSObject qui peut gérer les index de tableau de sorte que vous pouvez accéder à un objet imbriqué comme ceci: « person.friends [0] .name »

@interface NSObject (ValueForKeyPathWithIndexes) 
    -(id)valueForKeyPathWithIndexes:(NSString*)fullPath; 
@end 


#import "NSObject+ValueForKeyPathWithIndexes.h"  
@implementation NSObject (ValueForKeyPathWithIndexes) 

-(id)valueForKeyPathWithIndexes:(NSString*)fullPath 
{ 
    NSRange testrange = [fullPath rangeOfString:@"["]; 
    if (testrange.location == NSNotFound) 
     return [self valueForKeyPath:fullPath]; 

    NSArray* parts = [fullPath componentsSeparatedByString:@"."]; 
    id currentObj = self; 
    for (NSString* part in parts) 
    { 
     NSRange range1 = [part rangeOfString:@"["]; 
     if (range1.location == NSNotFound)   
     { 
      currentObj = [currentObj valueForKey:part]; 
     } 
     else 
     { 
      NSString* arrayKey = [part substringToIndex:range1.location]; 
      int index = [[[part substringToIndex:part.length-1] substringFromIndex:range1.location+1] intValue]; 
      currentObj = [[currentObj valueForKey:arrayKey] objectAtIndex:index]; 
     } 
    } 
    return currentObj; 
} 
@end 

Utilisez comme si

NSString* personsFriendsName = [obj valueForKeyPathsWithIndexes:@"me.friends[0].name"]; 

Il n'y a pas de vérification d'erreur, donc il est sujet à casser mais vous avez l'idée.

+4

Sorte de non pertinent maintenant que nous avons la syntaxe littérale Obj-C améliorée. Vous pouvez écrire l'instruction originale dans ma question sous la forme: NSDictionary * address = dictionary [@ "Placemark"] [0] [@ "address"]; ' – jsd

+2

Oui, la nouvelle syntaxe est super. J'utilise mon exemple ci-dessus dans un scénario où une structure de données externe peut changer, donc un sélecteur de chaîne est très utile. – psy

+0

@jsd Il peut toujours être utile lorsqu'il est utilisé avec Cocoa Bindings ou avec des opérateurs tels que '@ sum'. Surtout lorsqu'il est utilisé avec des étiquettes de débogage temporaires. –

3

Vous pouvez intercepter le mot de passe dans l'objet contenant le NSArray.

Dans votre cas, le chemin d'accès clavier deviendrait Placemark0.address ... Override valueForUndefinedKey; recherchez l'index dans le keypath; quelque chose comme ceci:

-(id)valueForUndefinedKey:(NSString *)key 
{ 
    // Handle paths like Placemark0, Placemark1, ... 
    if ([key hasPrefix:@"Placemark"]) 
    { 
     // Caller wants to access the Placemark array. 
     // Find the array index they're after. 
     NSString *indexString = [key stringByReplacingOccurrencesOfString:@"Placemark" withString:@""]; 
     NSInteger index = [indexString integerValue]; 

     // Return array element. 
     if (index < self.placemarks.count) 
      return self.placemarks[index]; 
    } 

    return [super valueForUndefinedKey:key]; 
} 

Cela fonctionne très bien pour les frameworks de modèles, par ex. Mantle.