2009-12-08 14 views
37

Me gustaría probar si un objeto tiene una propiedad @ editable en el SDK de iPhone.¿Cómo se puede saber si existe una clave para un objeto mediante la codificación de clave-valor?

Una posible forma de hacerlo es comprobar el método -valueForKey: ¡pero parece bastante poco elegante!

Ejemplo:

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

¿Hay una mejor manera de hacer esto?

+0

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

Respuesta

49

Si está creando el objeto que se está revisando, puede anular valueForUndefinedKey: y setValue:forUndefinedKey para hacer algo más útil que generar una excepción.

Si, por otro lado, está tratando de introspectar objetos que no conoce en tiempo de ejecución, tendrá que utilizar los métodos de tiempo de ejecución para hacerlo. Puede utilizar el tiempo de ejecución object-c mismo y llamar al class_copyPropertyList o al protocol_copyPropertyList y ocuparse de ellos, o usar Foundation y llamar al respondsToSelector en el objeto para el getter/setters KVC para una propiedad dada, por ejemplo, para una propiedad foo que llamaría algo así como [someObject respondsToSelector:NSSelectorFromString(@"foo")];.

5

No hay manera, en general, de determinar si un objeto tiene un valor para una clave determinada. Una instancia puede decidir devolver un valor para una clave no definida de otro modo a partir de su método -valueForUndefinedKey:. O puede permitir que la implementación predeterminada arroje una excepción. Objective-C moderno (2.0) objetos a menudo declaran propiedades relevantes en su API pública. Para estos objetos, puede utilizar class_copyPropertyList o protocol_copyPropertyList del tiempo de ejecución de Objective-C para obtener una lista de las propiedades disponibles. También puede emular la lista de búsqueda de KVC, probando a través del repondsToSelector: si la instancia responde a un método getter apropiado. Finalmente, puede usar class_copyIvarList del tiempo de ejecución para verificar los ivars para un ivar apropiado. Este es un lote del trabajo que no debería estar haciendo. No se garantiza que permite saber si una instancia de objeto lanzará una excepción cuando se envía un mensaje valueForKey: e indica un problema más grande ...

Si usted tiene una colección de objetos de todo lo que debe proporcionar un valor para un determinado clave, pero algunos no, tienes un problema de diseño. Debe definir una interfaz adecuada (un @protocol en Objective-C) que declare las propiedades necesarias. Luego puede probar la conformidad con este protocolo, o hacer uso del compilador para verificar el tipo de tiempo en tiempo de compilación para asegurarse de que la instancia se ajuste al protocolo (si está pasando por instancias únicas).

+0

¡Quizás ayudaría a aclarar! La aplicación se ejecuta en múltiples tiempos de ejecución, con diferentes versiones de bibliotecas disponibles. En una versión, una propiedad que necesito establecer (de tipo primitivo) no existe; de ​​ahí el requisito de verificar si existe. – Ted

+3

Si controla las clases en cuestión y sabe que la clase * responderá * a un selector apropiado en la versión de biblioteca que incluye su propiedad, use 'respondsToSelector:'. En este caso, debe usar el método getter directamente en lugar de 'valueForKey:' ya que será más rápido. –

+0

Desafortunadamente, no controlo las clases en cuestión. Para facilitar las cosas, he cambiado al uso de 'NSInvocation' para establecer la propiedad de tipo primitivo en lugar de usar la codificación de valor-clave (y de hecho estoy usando' respondsToSelector: '). – Ted

0

Siguiendo de @Jason Coco answer.

Aquí está mi oferta sobre cómo verificar específicamente la existencia de selectores "establecidos" de la propiedad que está tratando de establecer.

Esto es pura porquería pero funciona y me he encontrado utilizándolo. Usted ha sido advertido ..

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

Uso:

en Swift: añadir el fragmento de código a la cabecera de puente a continuación:

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

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

¿Por qué no usar simplemente 'NSSelectorFromString (" setPropertyName: ")' en 'Swift'? –

+0

un punto válido, pero parece producir una advertencia molesta. –

+0

Solo 'Selector (" selectorName: ")' genera una advertencia. 'NSSelectorFromString (...)' no. Obviamente, no es más seguro de ninguna manera. –

Cuestiones relacionadas