获取字符串形式的属性名称

发布于 2024-11-19 03:10:27 字数 222 浏览 11 评论 0原文

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

@property (nonatomic, retain) MyObject *crazyObject;

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

I need a way to pass a property and get the name assigned to it. Any suggestions?

@property (nonatomic, retain) MyObject *crazyObject;

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

纵情客 2024-11-26 03:10:27

你可以试试这个:

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

You can try this:

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);
蝶舞 2024-11-26 03:10:27

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

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

然后像这样使用它:

NSString *strProp = STR_PROP(myProperty);

It's as simple as this...expanding upon what Chuck already mentioned:

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

You then use it like so:

NSString *strProp = STR_PROP(myProperty);
天生の放荡 2024-11-26 03:10:27

背景

请记住,属性实际上只是引用 Apple,“声明类的访问器方法的语法简写。”事实上,@property 声明本身根本不起作用。您的 @synthesize 语句将 @property 转换为两种方法的等效项:

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

使用哪一种取决于 self.crazyObject 周围的上下文。 (如果您自己没有这样做,@synthesize 也会创建一个匹配的实例变量。)所有这一切的后果是您无法真正使用一种方法在属性之间进行转换。

建议的解决方案

您可以使用 Apple 已经提供的内容:

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

或者执行一些自定义操作:

假设 self.crazyObject 实际上会转换为 [self madObject][self setCrazyObject: foo] 当你的代码运行时,你可能需要两个方法,例如:

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

然后你可能需要至少 2 个伴随方法,例如:

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

在这些方法中,你可以使用 基础函数 NSStringFromSelectorNSSelectorFromString 在 SEL 和 NSString 之间来回转换。使用您喜欢的任何字符串操作在 setter 字符串 (setCrazyObject) 和属性名称 (crazyObject) 之间来回转换。

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

Background

Keep in mind that properties are really just, to quote Apple, "a syntactical shorthand for declaring a class’s accessor methods." In fact, by itself, the @property declaration doesn't even work. Your @synthesize statement translates the @property into the equivalent of two methods:

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

Which one is used depends on the context surrounding your self.crazyObject. (@synthesize also creates a matching instance variable if you didn't do it yourself.) The offshoot of all this is that you can't really translate to and from a property with one single method.

Proposed Solution

You can use what Apple already provides:

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

Or do something custom:

Given that self.crazyObject really translates to either [self crazyObject] or [self setCrazyObject:foo] by the time your code is running, ou'll probably need two methods, like:

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

You might then want at least 2 companion methods such as:

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

Within these methods, you can use the Foundation functions NSStringFromSelector and NSSelectorFromString to convert back and forth between SEL and NSString. Use whatever string manipulations you like to convert back and forth between your setter string (setCrazyObject) and your property name (crazyObject).

A complete solution is hard to provide without knowing the exact use case, but hopefully this provides some more clues for anyone trying to accomplish something similar. There might even be some useful things made possible by combining this approach with Oscar's answer.

断舍离 2024-11-26 03:10:27

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

Here is a function that returns the name of an ivar, so basically it not only returns the properties but any ivar of the class. I haven't found a way to get the property directly so I used the ivar trick.

#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;
}
不乱于心 2024-11-26 03:10:27

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

添加了 #import

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

After searching and debugging i find solution for me...

Added #import <objc/runtime.h>

Methods object_getIvar(id obj, Ivar ivar) send bad access and app crashes. i modify some code and it worked great:

+(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;
}
兔姬 2024-11-26 03:10:27

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

Modifying the solution, it works when your object is allocated already, otherwise it returns 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;
}
洋洋洒洒 2024-11-26 03:10:27

您可以使用

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

这种方法的好处是:

  1. Xcode 会为您自动补全单词 crazyObject
  2. 当稍后您将属性名称从 crazyObject 更改为 myCrazyObject 时,Xcode 将添加一条警告,指出 “无法识别的选择器!” —— 非常好用于调试。

我经常使用这种方法,我什至创建了一个函数,它允许写更少的字母:

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

现在你的最终解决方案可以如下所示:

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

You can use

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

The good thing about this approach is that:

  1. Xcode will autocomplete word crazyObject for you.
  2. When later on you will change the property name from crazyObject to myCrazyObject, Xcode will add a warning saying "unrecognized selector!" -- pretty good for debugging.

I use this method so often, that I even created a function, which allows to write less letters:

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

Now your final solution can look like this:

NSString *str = sfs(@selector(crazyObject));
递刀给你 2024-11-26 03:10:27

来自 以字符串形式获取属性名称,而不使用运行时参考库,只需定义:

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

然后你可以执行如下操作:

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

From Get property name as string, without using the runtime reference library, just define:

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

And then you can do something like this:

 NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
泼猴你往哪里跑 2024-11-26 03:10:27

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

You may check my approach at Gist to get the string for a property with autocompletion and compile-time check.

How to use:

Get the property name for a class:

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

Get the property name for a protocol:

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

// C string for a protocol
PropertyNameForProtocol(AnyProtocol, date);    // ==> "date"
// NSString for a protocol
PropertyStringForProtocol(AnyProtocol, date);  // ==> @"date"
丶情人眼里出诗心の 2024-11-26 03:10:27

非常规的、老套的、丑陋的、迟到的,但是......尽管它的名字很响亮,但它的作用就像一个魅力:

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

Unconventional, hacky, ugly, late, but... as strong-named as it gets and works like a charm:

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

Sample usage:

NSLog(SOME_WAY_TO_GET_PROPERTY_NAME(self.customer.surname)); // surname
NSLog(SOME_WAY_TO_GET_PROPERTY_NAME([[self customer] birthDate])); // birthDate
...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文