25

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

@property (nonatomic, retain) MyObject *crazyObject;

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

10 回答 10

24

你可以试试这个:

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);
于 2011-07-07T19:18:12.170 回答
20

就这么简单……扩展查克已经提到的内容:

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

然后你像这样使用它:

NSString *strProp = STR_PROP(myProperty);
于 2012-09-27T13:57:13.657 回答
15

背景

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

如果不知道确切的用例,很难提供一个完整的解决方案,但希望这能为任何试图完成类似事情的人提供更多线索。通过将这种方法与奥斯卡的答案结合起来,甚至可能会有一些有用的事情成为可能。

于 2011-10-20T04:16:35.223 回答
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;
}
于 2011-07-07T19:30:03.850 回答
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;
}
于 2014-04-19T23:40:44.930 回答
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;
}
于 2014-04-23T06:37:41.623 回答
2

您可以使用

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));
于 2016-02-05T23:07:40.227 回答
1

Get property name as string 中,不使用运行时参考库,只需定义:

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

然后你可以做这样的事情:

 NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
于 2015-06-17T09:40:40.773 回答
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"
于 2015-08-28T09:15:46.313 回答
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
...
于 2019-02-07T11:38:47.757 回答