【问题标题】:Get property name as a string以字符串形式获取属性名称
【发布时间】:2011-09-30 17:52:00
【问题描述】:

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

@property (nonatomic, retain) MyObject *crazyObject;

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

【问题讨论】:

  • 在代码SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject) 中,crazyObject 不是属性——它是一个实例变量,是函数的参数。在函数内部,它只是一个与实例变量具有相同值的参数。实际上获取属性本身以便将其传递给函数将涉及更多代码。就目前而言,您所要求的基本上是将SOME_WAY_TO_GET_PROPERTY_NAME() 转换为@""。这实际上是可行的——#define SOME_WAY_TO_GET_PROPERTY_NAME(n) @#n——但不是很有用。你能解释一下这个用例吗?
  • 我用 _ 定义我的实例变量,例如 @synthesize crazyObject = _crazyObject;
  • 那么crazyObject 在这种情况下没有任何意义。它仍然无法识别属性。再说一遍:你能解释一下这个用例吗?您要做什么尚不清楚,因为如果您有要传递给此函数的属性名称,则可以将其括在引号中。

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


【解决方案1】:

你可以试试这个:

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

【讨论】:

  • 这将打印所有属性名称。一个类没有反思的方式来说“我应该为哪个属性返回这个值?”除了查询所有属性和比较值。
  • @Tommy 是的,您必须比较这些值,我不知道它是否可以完全按照@aryaxt 的要求完成。
  • [name isEqualToString:@"crazyObject"] 单独使用并不是非常有用,但为[[name substringToIndex:[@"crazy" length]] isEqualToString:@"crazy"] 之类的东西提供了可能性
【解决方案2】:

这是一个返回 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;
}

【讨论】:

  • 如果多个 ivar 具有相同的值,或者即使您将对象 ivar 设置为 nil 并将整数 ivar 设置为 0,这将中断。
  • @Chuck: 没错,但是任何一种可以解决问题的方法都是如此。
【解决方案3】:

背景

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

如果不知道确切的用例,很难提供完整的解决方案,但希望这可以为任何尝试完成类似事情的人提供更多线索。通过将这种方法与 Oscar 的答案相结合,甚至可能会产生一些有用的东西。

【讨论】:

  • 并非如此。从 Objective-C 运行时的角度来看,正式声​​明的属性本身就是一个实体。它有自己的属性(例如所有权),而不是任意一对 getter -X 和 setter -setX:.
  • 我相信你的话,因为我的回答是基于不知何时的模糊记忆。我确实希望 Apple 提供相当于 NSSelectorFromStringNSStringFromSelector 的属性。
  • @clozach - 苹果确实做到了。 NSString *foo = NSSelectorFromString(@selector(myClassProperty));
  • 这将有助于避免很多错别字
  • 你的代码有错误:NSString *foo = NSSelectorFromString(@selector(myClassProperty)); 应该是NSString *foo = NSStringFromSelector(@selector(myClassProperty));
【解决方案4】:

就这么简单...扩展 Chuck 已经提到的内容:

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

然后你像这样使用它:

NSString *strProp = STR_PROP(myProperty);

【讨论】:

  • 这可行,但是如果该属性已从类中删除怎么办?理想的解决方案是在运行之前而不是在运行期间进行检查。
  • @Arvin 避免字符串类型代码的绝佳解决方案。
  • 此方法存在一些问题:corporationunknown.com/blog/2014/03/24/…
【解决方案5】:

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

已添加#import &lt;objc/runtime.h&gt;

方法 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;
}

【讨论】:

    【解决方案6】:

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

    【讨论】:

    • 我在使用此方法时有时会收到 EXEC_BAD_ACCESS 异常,但只是有时。当转移到 Obj-C 风格的方法时,我停止了异常。
    【解决方案7】:

    来自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
    

    【讨论】:

    • 我不明白这有什么帮助?您正在手动输入属性名称,它会返回您输入的内容。这就像去商店支付 5 美元的钞票并获得 5 美元的钞票一样。
    • 不错的解决方案。谢谢!
    【解决方案8】:

    您可以在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"
    

    【讨论】:

      【解决方案9】:

      你可以使用

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

      【讨论】:

        【解决方案10】:

        非常规、老套、丑陋、迟到,但是......尽其所能,并像魅力一样工作:

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

        【讨论】:

          猜你喜欢
          • 2012-11-16
          • 2015-03-18
          • 1970-01-01
          • 1970-01-01
          • 2015-05-27
          • 2014-05-23
          • 2011-04-16
          相关资源
          最近更新 更多