Получить имя свойства в виде строки - PullRequest
25 голосов
/ 07 июля 2011

Мне нужен способ передать свойство и получить присвоенное ему имя.Есть предложения?

@property (nonatomic, retain) MyObject *crazyObject;

NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return @"crazyObject"

Ответы [ 10 ]

23 голосов
/ 07 июля 2011

Вы можете попробовать это:

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);
20 голосов
/ 27 сентября 2012

Это так же просто, как это ..., расширяя то, что Чак уже упоминал:

#ifndef STR_PROP
    #define STR_PROP( prop ) NSStringFromSelector(@selector(prop))
#endif

Затем вы используете это так:

NSString *strProp = STR_PROP(myProperty);
14 голосов
/ 20 октября 2011

Фон

Имейте в виду, что свойства на самом деле, по словам , Apple , "синтаксическая стенография для объявления методов доступа класса". Фактически, само по себе объявление @property даже не работает. Ваш оператор @synthesize переводит @property в эквивалент двух методов:

- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;

Какой из них использовать, зависит от контекста, окружающего ваш self.crazyObject. (@synthesize также создает соответствующую переменную экземпляра, если вы сами этого не сделали.) Результат всего этого заключается в том, что вы не можете реально преобразовать свойство в свойство и из него одним единственным методом.

Предлагаемое решение

Вы можете использовать то, что Apple уже предоставляет:

NSString *foo = NSStringFromSelector(@selector(myClassProperty));

Или сделать что-то нестандартное:

Учитывая, что self.crazyObject действительно преобразуется в [self crazyObject] или [self setCrazyObject:foo] к тому времени, когда ваш код выполняется, вам, вероятно, понадобятся два метода, например:

- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;

Затем вы можете использовать как минимум 2 сопутствующих метода, например:

- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;

В этих методах вы можете использовать функции Foundation NSStringFromSelector и NSSelectorFromString для преобразования туда и обратно между SEL и NSString. Используйте любые манипуляции со строками, которые вам нравятся, чтобы конвертировать туда и обратно между вашей сеттерной строкой (setCrazyObject) и именем вашего свойства (crazyObject).

Полное решение сложно предоставить, не зная точного варианта использования, но, надеюсь, это даст некоторые подсказки для тех, кто пытается сделать что-то подобное. Возможно, даже некоторые полезные вещи станут возможными благодаря объединению этого подхода с ответом Оскара.

4 голосов
/ 07 июля 2011

Вот функция, которая возвращает имя ивара, поэтому в основном она возвращает не только свойства, но и любой ивар класса. Я не нашел способа получить собственность напрямую, поэтому использовал трюк с иваром.

#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;
}
3 голосов
/ 20 апреля 2014

После поиска и отладки я нахожу решение для меня ...

Добавлено #import <objc/runtime.h>

Методы object_getIvar (id obj, Ivar ivar) отправляют неверный доступ и происходит сбой приложения.Я изменил некоторый код, и он прекрасно работал:

+(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;
}
2 голосов
/ 23 апреля 2014

Модифицируя решение, оно работает, когда ваш объект уже выделен, иначе возвращает 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;
}
1 голос
/ 06 февраля 2016

Вы можете использовать

NSString *str = NSStringFromSelector(@selector(crazyObject));

Хорошая особенность этого подхода в том, что:

  1. Xcode автоматически заполняет слово crazyObject для вас.
  2. Когда позже вы измените имя свойства с crazyObject на myCrazyObject, Xcode добавит предупреждение, говорящее "unrecognized selector!" - очень хорошо для отладки.

Я использую этот метод так часто, что даже создал функцию, которая позволяет писать меньше букв:

NSString * __nonnull sfs(SEL __nonnull theSelector)
{
    if (!theSelector)
    {
        abort();
    }
    return NSStringFromSelector(theSelector);
}

Теперь ваше окончательное решение может выглядеть так:

NSString *str = sfs(@selector(crazyObject));
1 голос
/ 28 августа 2015

Вы можете проверить мой подход на Суть , чтобы получить строку для свойства с автозаполнением и проверкой во время компиляции.

Как использовать:

Получить имя свойства для класса :

@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";

Получить имя свойства для протокола :

@protocol AnyProtocol
@property (strong) NSDate *date;
@end

// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date);    // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date);  // ==> @"date"
1 голос
/ 17 июня 2015

С Получить имя свойства в виде строки без использования библиотеки ссылок времени выполнения , просто определите:

#define propertyKeyPath(property) (@""#property)
#define propertyKeyPathLastComponent(property) [[(@""#property) componentsSeparatedByString:@"."] lastObject]

И тогда вы можете сделать что-то вроде этого:

 NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
0 голосов
/ 07 февраля 2019

Нетрадиционный, хакерский, некрасивый, поздний, но ... с таким сильным именем, как он получает и работает как заклинание:

#define SOME_WAY_TO_GET_PROPERTY_NAME(p) p == p ? [[[[[[[NSString alloc] initWithCString:#p encoding:NSUTF8StringEncoding] componentsSeparatedByString:@"."] lastObject] componentsSeparatedByString:@" "] lastObject] stringByReplacingOccurrencesOfString:@"]" withString:@""] : @""

Пример использования:

NSLog(SOME_WAY_TO_GET_PROPERTY_NAME(self.customer.surname)); // surname
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME([[self customer] birthDate])); // birthDate
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...