获取属性名称作为字符串

时间:2011-07-07 19:14:10

标签: objective-c introspection objective-c-runtime declared-property

我需要一种传递属性并获取分配给它的名称的方法。有什么建议吗?

@property (nonatomic, retain) MyObject *crazyObject;

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

10 个答案:

答案 0 :(得分:23)

你可以试试这个:

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

答案 1 :(得分:20)

就像这一样简单......扩展了查克已经提到的内容:

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

然后你就这样使用它:

NSString *strProp = STR_PROP(myProperty);

答案 2 :(得分:14)

<强>背景

请记住,对于quote 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 functions NSStringFromSelectorNSSelectorFromString在SEL和NSString之间来回转换。使用您喜欢的任何字符串操作在您的setter字符串(setCrazyObject)和您的属性名称(crazyObject)之间来回转换。

在不知道具体用例的情况下很难提供完整的解决方案,但希望这为任何试图完成类似工作的人提供了更多线索。将这种方法与奥斯卡的答案相结合,甚至可能会有一些有用的东西成为可能。

答案 3 :(得分:4)

这是一个返回ivar名称的函数,所以基本上它不仅返回属性,还返回类的任何ivar。我没有找到直接获得财产的方法所以我使用了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;
}

答案 4 :(得分:3)

搜索和调试后,我找到了解决方案......

添加了#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;
}

答案 5 :(得分:2)

修改解决方案,它在您的对象已经分配时有效,否则返回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;
}

答案 6 :(得分:1)

Get property name as string, without using the runtime reference library开始,只需定义:

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

然后你可以这样做:

 NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street

答案 7 :(得分:1)

您可以在Gist检查我的方法,以获取具有自动完成和编译时检查的属性的字符串。

如何使用:

获取的属性名称:

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

答案 8 :(得分:1)

您可以使用

```{r}
library(dygraphs)
lungDeaths <- cbind(mdeaths, fdeaths)
res <- lapply(1:2, function(i) dygraph(lungDeaths[, i]))
htmltools::tagList(res)
```

这种方法的好处在于:

  1. Xcode会为您自动填写单词NSString *str = NSStringFromSelector(@selector(crazyObject));
  2. 稍后您将属性名称从crazyObject更改为crazyObject,Xcode会添加警告myCrazyObject - 非常适合调试。
  3. 我经常使用这种方法,甚至创建了一个函数,它允许写更少的字母:

    "unrecognized selector!"

    现在您的最终解决方案可能如下所示:

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

答案 9 :(得分:0)

非常规的,hacky的,丑陋的,迟到的,但是...就像它的名字一样具有强大的魅力,就像魅力一样:

#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
...