objective-c - studio - superponer graficas en r
Obtener el nombre de la propiedad como una cadena (9)
Necesito una forma de pasar una propiedad y obtener el nombre asignado. ¿Alguna sugerencia?
@property (nonatomic, retain) MyObject *crazyObject;
NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return @"crazyObject"
Al modificar la solución, funciona cuando su objeto ya está asignado, de lo contrario, devuelve nil: -
NSString * NSStringFromProperty(NSObject* property, NSObject* class)
{
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([class class], &propertyCount);
NSString *name = nil;
for (unsigned int i = 0; i < propertyCount; ++i)
{
name = [NSString stringWithUTF8String:property_getName(properties[i])];
NSObject *object = [class valueForKey:name];
if (object != nil && object == property)
{
break;
}
else
{
name = nil;
}
}
free(properties);
return name;
}
Aquí hay una función que devuelve el nombre de un ivar, así que básicamente no solo devuelve las propiedades sino cualquier ivar de la clase. No he encontrado una forma de obtener la propiedad directamente, así que usé el truco de ivar.
#import <objc/objc.h>
/// -----
- (NSString *)nameOfIvar:(id)ivarPtr
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([self class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
id pointer = object_getIvar(self, ivar);
if(pointer == ivarPtr)
{
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
break;
}
}
free(ivars);
}
return name;
}
Desde Obtener nombre de propiedad como cadena, sin usar la biblioteca de referencia de tiempo de ejecución , solo defina:
#define propertyKeyPath(property) (@""#property)
#define propertyKeyPathLastComponent(property) [[(@""#property) componentsSeparatedByString:@"."] lastObject]
Y luego puedes hacer algo como esto:
NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
Después de buscar y depurar, encuentro una solución para mí ...
Se agregó #import <objc/runtime.h>
Los métodos object_getIvar (id obj, Ivar ivar) envían acceso incorrecto y bloqueos de aplicaciones. Modifico un código y funcionó de maravilla:
+(NSString*)stringWithProperty:(id)property withClass:(id)controller
{
NSString *name = nil;
uint32_t ivarCount;
Ivar *ivars = class_copyIvarList([controller class], &ivarCount);
if(ivars)
{
for(uint32_t i=0; i<ivarCount; i++)
{
Ivar ivar = ivars[i];
name = [NSString stringWithUTF8String:ivar_getName(ivar)];
if ([controller valueForKey:name] == property)
{
break;
}
}
free(ivars);
}
return name;
}
Es tan simple como esto ... expandir sobre lo que Chuck ya mencionó:
#ifndef STR_PROP
#define STR_PROP( prop ) NSStringFromSelector(@selector(prop))
#endif
Luego lo usa así:
NSString *strProp = STR_PROP(myProperty);
Puede consultar mi enfoque en Gist para obtener la cadena de una propiedad con autocompletado y verificación en tiempo de compilación.
Cómo utilizar:
Obtener el nombre de la propiedad para una clase :
@interface AnyClass : NSObject
@property (strong) NSData *data;
@end
// == My approach ==
// C string for a class
PropertyNameForClass(AnyClass, data); // ==> "data"
// NSString for a class
PropertyStringForClass(AnyClass, data); // ==> @"data"
// Bad approach (no autocompletion; no compile-time check):
NSString *propertyName = @"data";
Obtener el nombre de la propiedad para un protocolo :
@protocol AnyProtocol
@property (strong) NSDate *date;
@end
// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date); // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date); // ==> @"date"
Puedes intentar esto:
unsigned int propertyCount = 0;
objc_property_t * properties = class_copyPropertyList([self class], &propertyCount);
NSMutableArray * propertyNames = [NSMutableArray array];
for (unsigned int i = 0; i < propertyCount; ++i) {
objc_property_t property = properties[i];
const char * name = property_getName(property);
[propertyNames addObject:[NSString stringWithUTF8String:name]];
}
free(properties);
NSLog(@"Names: %@", propertyNames);
Puedes usar
NSString *str = NSStringFromSelector(@selector(crazyObject));
Lo bueno de este enfoque es que:
- Xcode autocompletará la palabra
crazyObject
por usted. - Cuando más tarde cambie el nombre de la propiedad de
crazyObject
amyCrazyObject
, Xcode agregará una advertencia que diga"unrecognized selector!"
- bastante bueno para la depuración.
Utilizo este método tan a menudo, que incluso creé una función, que permite escribir menos letras:
NSString * __nonnull sfs(SEL __nonnull theSelector)
{
if (!theSelector)
{
abort();
}
return NSStringFromSelector(theSelector);
}
Ahora su solución final puede verse así:
NSString *str = sfs(@selector(crazyObject));
Fondo
Tenga en cuenta que las propiedades son realmente justas, para citar a Apple , "una abreviatura sintáctica para declarar los métodos de acceso de una clase". De hecho, por sí mismo, la declaración @property ni siquiera funciona. Su instrucción @synthesize
traduce @property
en el equivalente de dos métodos:
- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;
El que se usa depende del contexto que rodea a su self.crazyObject. ( @synthesize
también crea una variable de instancia coincidente si no lo hiciste tú mismo). La consecuencia de todo esto es que no puedes traducir desde y hacia una propiedad con un solo método.
Solución propuesta
Puede usar lo que Apple ya proporciona:
NSString *foo = NSStringFromSelector(@selector(myClassProperty));
O haz algo personalizado:
Dado que self.crazyObject realmente se traduce en [self crazyObject]
o [self setCrazyObject:foo]
cuando se ejecuta su código, probablemente necesite dos métodos, como:
- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;
Es posible que desee al menos 2 métodos complementarios, como:
- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;
Dentro de estos métodos, puede usar las funciones Foundation NSStringFromSelector
y NSSelectorFromString
para convertir entre SEL y NSString. Utilice las manipulaciones de cadena que desee para convertir hacia adelante y hacia atrás entre su cadena setter (setCrazyObject) y su nombre de propiedad (crazyObject).
Es difícil proporcionar una solución completa sin conocer el caso de uso exacto, pero es de esperar que esto proporcione algunas pistas más para cualquiera que intente lograr algo similar. Incluso podría haber algunas cosas útiles hechas posible combinando este enfoque con la respuesta de Oscar.