2009-12-08 3 views
37

Je voudrais tester si un objet a une propriété @ inscriptible dans le SDK de l'iPhone.Comment savoir si une clé existe pour un objet à l'aide du codage de valeur-clé?

Une façon possible de faire ceci est de vérifier la méthode -valueForKey: mais cela semble plutôt inélégant!

Exemple:

@try { 
    id *value = [instance valueForKey:@"myProperty"]; 
    } 
    @catch (NSException * e) { 
    // Key did not exist 
    } 

Y at-il une meilleure façon de le faire?

+0

http: // stackoverflow.com/a/3059053/294884 – Fattie

Répondre

49

Si vous créez l'objet en cours de vérification, vous pouvez remplacer valueForUndefinedKey: et setValue:forUndefinedKey par quelque chose de plus utile que de déclencher une exception.

Si, par contre, vous essayez d'introspecter des objets que vous ne connaissez pas à l'exécution, vous devrez utiliser les méthodes d'exécution pour le faire. Vous pouvez soit utiliser le runtime objectif-c lui-même et appeler class_copyPropertyList ou et traiter avec ceux-ci, ou utiliser Foundation et appelez respondsToSelector sur l'objet pour le getter/setters KVC pour une propriété donnée, par exemple, pour une propriété foo vous appelez quelque chose comme [someObject respondsToSelector:NSSelectorFromString(@"foo")];.

5

En général, il n'est pas possible de déterminer si un objet a une valeur pour une clé donnée. Une instance peut décider de renvoyer une valeur pour une clé non définie à partir de sa méthode -valueForUndefinedKey:. Ou il peut laisser l'implémentation par défaut jeter une exception. Les objets Objective-C (2.0) modernes souvent déclarent des propriétés pertinentes dans leur API publique. Pour ces objets, vous pouvez utiliser les codes class_copyPropertyList ou protocol_copyPropertyList de l'environnement d'exécution d'Objective-C pour obtenir la liste des propriétés disponibles. Vous pouvez également émuler la liste de recherche KVC en testant via repondsToSelector: si l'instance répond à une méthode getter appropriée. Enfin, vous pouvez utiliser class_copyIvarList de l'exécution pour vérifier les ivars pour un ivar approprié. Ceci est un lot de travail que vous ne devrait pas faire. Il ne garantit pas que vous savez si une instance d'objet déclenchera une exception lors de l'envoi d'un message valueForKey: et cela indique un problème plus important ...

Si vous avez une collection d'objets qui doivent tous fournir une valeur pour un clé, mais certains ne le font pas, vous avez un problème de conception. Vous devez définir une interface appropriée (un @protocol dans Objective-C) qui déclare les propriétés nécessaires. Vous pouvez ensuite tester la conformité à ce protocole ou utiliser le compilateur pour effectuer une vérification de type à la compilation afin de vous assurer que l'instance est conforme au protocole (si vous utilisez des instances uniques).

+0

Peut-être que cela aiderait à clarifier! L'application s'exécute sur plusieurs runtimes, avec différentes versions de bibliothèques disponibles. Dans une version, une propriété que je dois définir (de type primitif) n'existe pas - d'où l'obligation de vérifier si elle existe. – Ted

+3

Si vous contrôlez les classes en question et si vous savez que la classe * répondra * à un sélecteur approprié dans la version de la bibliothèque qui inclut votre propriété, utilisez 'respondsToSelector:'. Dans ce cas, vous devriez utiliser la méthode getter directement à la place de 'valueForKey:' car elle sera plus rapide. –

+0

Malheureusement, je ne contrôle pas les classes en question. Pour simplifier les choses, je suis passé à l'utilisation de 'NSInvocation' pour définir la propriété primitivement typée au lieu d'utiliser le codage Key-Value (et j'utilise en effet' respondsToSelector: '). – Ted

0

Suite de @Jason Coco réponse.

Voici mon offre sur la façon de vérifier spécifiquement l'existence des sélecteurs "set" de la propriété que vous essayez de définir.

C'est de la crasse mais ça marche et je me suis retrouvé à l'utiliser. Vous avez été prévenu ..

NS_INLINE SEL _Nonnull IBPropertySetSelectorForPropertyName(NSString*_Nonnull propertyName) 
{ 

    NSString* firstLetter = [propertyName substringToIndex:1]; 
    propertyName = [propertyName substringFromIndex:1]; 

    propertyName = [[NSString alloc] initWithFormat:@"set%@%@:",firstLetter.capitalizedString,propertyName]; 

    return NSSelectorFromString(propertyName); 
} 

Utilisation:

En rapide: Ajouter l'extrait de code à votre tête puis pont:

let key = "someKey"  
let sel = IBPropertySetSelectorForPropertyName(key) 

if SOMETHING.responds(to: sel) {SOMETHING.setValue(value, forKeyPath: key)} 
+0

Pourquoi ne pas simplement utiliser 'NSSelectorFromString (" setPropertyName: ")' dans 'Swift'? –

+0

un point valide mais il semble produire un avertissement embêtant. –

+0

Seul 'Selector (" selectorName: ")' génère un avertissement. 'NSSelectorFromString (...)' ne le fait pas. De toute évidence, ce n'est pas plus sûr. –

Questions connexes