プロパティを渡し、それに割り当てられた名前を取得する方法が必要です。助言がありますか?
@property (nonatomic, retain) MyObject *crazyObject;
NSString *str = SOME_WAY_TO_GET_PROPERTY_NAME(crazyObject);
// Above method should return @"crazyObject"
あなたはこれを試すことができます:
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);
それはこれと同じくらい簡単です...チャックがすでに述べたことを拡張します:
#ifndef STR_PROP
#define STR_PROP( prop ) NSStringFromSelector(@selector(prop))
#endif
その後、次のように使用します。
NSString *strProp = STR_PROP(myProperty);
背景
プロパティは、実際には 引用Apple に対して、「クラスのアクセサメソッドを宣言するための構文上の省略形」であることに注意してください。実際、@ property宣言だけでは機能しません。 @synthesize
ステートメントは、@property
を2つのメソッドに相当するものに変換します。
- (void)setCrazyObject:(MyObject *)something;
- (MyObject *)crazyObject;
どちらを使用するかは、self.crazyObjectを囲むコンテキストによって異なります。 (@synthesize
は、自分で実行しなかった場合も、一致するインスタンス変数を作成します。)これらすべての結果として、1つのメソッドでプロパティとの間で実際に変換することはできません。
提案されたソリューション
Appleがすでに提供しているものを使用できます:
NSString *foo = NSStringFromSelector(@selector(myClassProperty));
または何かカスタムを行います:
Self.crazyObjectがコードの実行時に実際に[self crazyObject]
または[self setCrazyObject:foo]
に変換されることを考えると、おそらく次のような2つのメソッドが必要になります。
- (NSString *)setterStringForProperty:(SEL)prop;
- (NSString *)getterStringForProperty:(SEL)prop;
次に、次のような少なくとも2つの関連メソッドが必要になる場合があります。
- (SEL)setterForPropertyName:(NSString *)propString;
- (SEL)getterForPropertyName:(NSString *)propString;
これらのメソッド内で、 Foundation functionsNSStringFromSelector
およびNSSelectorFromString
を使用して、SELとNSStringの間で相互に変換できます。好きな文字列操作を使用して、セッター文字列(setCrazyObject)とプロパティ名(crazyObject)の間で相互に変換します。
完全なソリューションを提供することは、正確なユースケースを知らずに提供することは困難ですが、うまくいけば、これが同様のことを達成しようとしている人にさらにいくつかの手掛かりを提供します。このアプローチとオスカーの答えを組み合わせることによって可能になるいくつかの便利なことさえあるかもしれません。
ここに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;
}
検索してデバッグした後、私は解決策を見つけます...
#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;
}
ソリューションを変更すると、オブジェクトがすでに割り当てられている場合に機能し、それ以外の場合は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;
}
ランタイム参照ライブラリを使用せずにプロパティ名を文字列として取得 から、次のように定義します。
#define propertyKeyPath(property) (@""#property)
#define propertyKeyPathLastComponent(property) [[(@""#property) componentsSeparatedByString:@"."] lastObject]
そして、あなたはこのようなことをすることができます:
NSLog(@"%@", propertyKeyPathLastComponent(appleStore.storeLocation.street)); //result: street
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"
使用できます
NSString *str = NSStringFromSelector(@selector(crazyObject));
このアプローチの良い点は次のとおりです。
crazyObject
を自動補完します。crazyObject
からmyCrazyObject
に変更すると、Xcodeは"unrecognized selector!"
-デバッグに適しています。私はこの方法を頻繁に使用しているため、文字数を減らすことができる関数を作成しました。
NSString * __nonnull sfs(SEL __nonnull theSelector)
{
if (!theSelector)
{
abort();
}
return NSStringFromSelector(theSelector);
}
これで、最終的なソリューションは次のようになります。
NSString *str = sfs(@selector(crazyObject));
型にはまらない、ハック、醜い、遅い、しかし...それが得られて魅力のように機能するのと同じくらい強力な名前:
#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
...