Objective-Cでオブジェクトプロパティリストを取得する


109

リストを取得するにはどうすればよいですか(NSArrayまたはNSDictionaryObjective-Cで特定のオブジェクトプロパティの)ですか?

次のシナリオを想像してみてください。プロパティとして、a、およびオブジェクトNSObjectを保持するNSString、だけを拡張する親クラスを定義しました。次に、この親クラスを拡張するクラスがいくつかあり、それぞれにさまざまなプロパティを追加しています。 BOOLNSData

私は上のインスタンスメソッドを実装することができどのような方法があり、親の言う、オブジェクト全体とリターンを通過するクラスでは、NSArrayと(子)クラスのプロパティのそれぞれのNSStringsものがありません、私は後でこれらを使用することができるように、親クラスにNSStringKVCの場合?

回答:


116

なんとか自分で答えが出ました。Obj-Cランタイムライブラリを使用することで、必要な方法でプロパティにアクセスできました。

- (void)myMethod {
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    for(i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithCString:propName
                                                                encoding:[NSString defaultCStringEncoding]];
            NSString *propertyType = [NSString stringWithCString:propType
                                                                encoding:[NSString defaultCStringEncoding]];
            ...
        }
    }
    free(properties);
}

これには、主にAppleコードサンプルから取得した「getPropertyType」C関数を作成する必要がありました(現時点では正確なソースを思い出せません)。

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T') {
            if (strlen(attribute) <= 4) {
                break;
            }
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "@";
}

5
これ以外の+1は、intなどのプリミティブでエラーになります。これと同じもののわずかに強化されたバージョンについては、以下の私の回答を参照してください。
jpswain 2011

1
正確さの問題として、[NSString stringWithCString:]は非推奨です[NSString stringWithCString:encoding:]
ゼケル

4
objcランタイムヘッダーをインポートする必要があります#import <objc / runtime.h> ARCで動作します。
Dae KIM、

Swiftを使用してそれを実現する方法を次に示します。
ラミス2017

76

@bolivaの答えは良いですが、int、long、float、doubleなどのプリミティブを処理するには少し余分なものが必要です。

私は彼からこの機能を追加するために構築しました。

// PropertyUtil.h
#import 

@interface PropertyUtil : NSObject

+ (NSDictionary *)classPropsFor:(Class)klass;

@end


// PropertyUtil.m
#import "PropertyUtil.h"
#import "objc/runtime.h"

@implementation PropertyUtil

static const char * getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    printf("attributes=%s\n", attributes);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // it's a C primitive type:
            /* 
                if you want a list of what will be returned for these primitives, search online for
                "objective-c" "Property Attribute Description Examples"
                apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.            
            */
            return (const char *)[[NSData dataWithBytes:(attribute + 1) length:strlen(attribute) - 1] bytes];
        }        
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // it's an ObjC id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // it's another ObjC object type:
            return (const char *)[[NSData dataWithBytes:(attribute + 3) length:strlen(attribute) - 4] bytes];
        }
    }
    return "";
}


+ (NSDictionary *)classPropsFor:(Class)klass
{    
    if (klass == NULL) {
        return nil;
    }

    NSMutableDictionary *results = [[[NSMutableDictionary alloc] init] autorelease];

    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(klass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithUTF8String:propName];
            NSString *propertyType = [NSString stringWithUTF8String:propType];
            [results setObject:propertyType forKey:propertyName];
        }
    }
    free(properties);

    // returning a copy here to make sure the dictionary is immutable
    return [NSDictionary dictionaryWithDictionary:results];
}




@end

1
#import <Foundation/Foundation.h>.hファイルの先頭に置くつもりでしたか?
Andrew

2
[NSStringのstringWithUTF8String:propType]は「propTypeのconstのchar * "のNSNumber \ X94 \ xfdkを解析できませんでした。それは、このような奇妙なのNSNumberである理由"とnilの文字列を返します...知らないMbのActiveRecordのため。?
Dumoko

見事!どうもありがとう。
Azik Abdullah

これは完璧です!
プラノイC 2018

28

@ orange80の答えには1つの問題があります。実際には、文字列が常に0で終了するとは限りません。これは、UTF8に変換しようとしたときにクラッシュするなどの予期しない結果につながる可能性があります(そのため、実際にはかなり厄介なクラッシュバグがありました。デバッグは楽しかったです^^)。実際に属性からNSStringを取得し、cStringUsingEncoding:を呼び出すことで修正しました。これは今では魅力のように機能します。(少なくとも私にとってもARCで動作します)

だからこれは今私のコードのバージョンです:

// PropertyUtil.h
#import 

@interface PropertyUtil : NSObject

+ (NSDictionary *)classPropsFor:(Class)klass;

@end


// PropertyUtil.m
#import "PropertyUtil.h"
#import <objc/runtime.h>

@implementation PropertyUtil

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    //printf("attributes=%s\n", attributes);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // it's a C primitive type:
            /*
             if you want a list of what will be returned for these primitives, search online for
             "objective-c" "Property Attribute Description Examples"
             apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.
             */
            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // it's an ObjC id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // it's another ObjC object type:
            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
    }
    return "";
}


+ (NSDictionary *)classPropsFor:(Class)klass
{
    if (klass == NULL) {
        return nil;
    }

    NSMutableDictionary *results = [[NSMutableDictionary alloc] init];

    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(klass, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithUTF8String:propName];
            NSString *propertyType = [NSString stringWithUTF8String:propType];
            [results setObject:propertyType forKey:propertyName];
        }
    }
    free(properties);

    // returning a copy here to make sure the dictionary is immutable
    return [NSDictionary dictionaryWithDictionary:results];
}

@end

@farthen提供したコードの問題を示す例を提供できますか?私はそれを見て興味があります。
jpswain 2013年

@ orange80まあ、AFAIRデータがゼロで終了することはありません。もしそうならこれは偶然に起こります。私は間違っているかもしれません。他のニュース:私はまだこのコードを実行していて、確実に実行されます:p
felinira 2013年

@ orange80 GoogleのIMA広告ライブラリからIMAAdRequestでバージョンを呼び出そうとしてこの問題に遭遇しました。farthenのソリューションはそれを解決しました。
Christopher Pickslay 14

ありがとう。前の2つの答えがうまくいかなかったとき、これはiOS7で私のために働きました。+1 for all 3.
ChrisH 2014

これが私にとってうまくいった唯一の答えです。プロパティタイプの "NSString \ x8d \ xc0 \ xd9"の奇妙さのような他のすべてが私に与えていました、おそらくchar *サイズ設定がオフだったためです
Brian Colavito

8

iOS 3.2で試したところ、プロパティの説明ではgetPropertyType関数がうまく機能しません。iOSのドキュメント「Objective-Cランタイムプログラミングガイド:宣言されたプロパティ」から例を見つけました。

iOS 3.2でのプロパティリストの改訂されたコードは次のとおりです。

#import <objc/runtime.h>
#import <Foundation/Foundation.h>
...
unsigned int outCount, i;
objc_property_t *properties = class_copyPropertyList([UITouch class], &outCount);
for(i = 0; i < outCount; i++) {
    objc_property_t property = properties[i];
    fprintf(stdout, "%s %s\n", property_getName(property), property_getAttributes(property));
}
free(properties);

7

ボリバの解決策はシミュレータでうまく機能することがわかりましたが、デバイスでは固定長の部分文字列が問題を引き起こしています。私は、デバイスで動作するこの問題のよりObjective-Cフレンドリーなソリューションを作成しました。私のバージョンでは、属性のC文字列をNSStringに変換し、それに対して文字列操作を実行して、型の説明のみの部分文字列を取得します。

/*
 * @returns A string describing the type of the property
*/

+ (NSString *)propertyTypeStringOfProperty:(objc_property_t) property {
    const char *attr = property_getAttributes(property);
    NSString *const attributes = [NSString stringWithCString:attr encoding:NSUTF8StringEncoding];

    NSRange const typeRangeStart = [attributes rangeOfString:@"T@\""];  // start of type string
    if (typeRangeStart.location != NSNotFound) {
        NSString *const typeStringWithQuote = [attributes substringFromIndex:typeRangeStart.location + typeRangeStart.length];
        NSRange const typeRangeEnd = [typeStringWithQuote rangeOfString:@"\""]; // end of type string
        if (typeRangeEnd.location != NSNotFound) {
            NSString *const typeString = [typeStringWithQuote substringToIndex:typeRangeEnd.location];
            return typeString;
        }
    }
    return nil;
}

/**
* @returns (NSString) Dictionary of property name --> type
*/

+ (NSDictionary *)propertyTypeDictionaryOfClass:(Class)klass {
    NSMutableDictionary *propertyMap = [NSMutableDictionary dictionary];
    unsigned int outCount, i;
    objc_property_t *properties = class_copyPropertyList(klass, &outCount);
    for(i = 0; i < outCount; i++) {
        objc_property_t property = properties[i];
        const char *propName = property_getName(property);
        if(propName) {

            NSString *propertyName = [NSString stringWithCString:propName encoding:NSUTF8StringEncoding];
            NSString *propertyType = [self propertyTypeStringOfProperty:property];
            [propertyMap setValue:propertyType forKey:propertyName];
        }
    }
    free(properties);
    return propertyMap;
}

NSRange const typeRangeStart = [attributes rangeOfString:@ "T @ \" "];にEXC_BAD_ACCESS例外をスローします//文字列の開始
Adam Mendoza

6

この実装は、Objective-CオブジェクトタイプとCプリミティブの両方で機能します。iOS 8対応です。このクラスは、3つのクラスメソッドを提供します。

+ (NSDictionary *) propertiesOfObject:(id)object;

オブジェクトのすべてのスーパークラスのプロパティを含む、オブジェクトのすべての表示可能なプロパティの辞書を返します。

+ (NSDictionary *) propertiesOfClass:(Class)class;

すべてのスーパークラスのプロパティを含む、クラスのすべての表示可能なプロパティの辞書を返します。

+ (NSDictionary *) propertiesOfSubclass:(Class)class;

サブクラスに固有のすべての表示可能なプロパティの辞書を返します。そのスーパークラスのプロパティ含まれ。

これらのメソッドの使用の1つの有用な例は、オブジェクトをObjective-Cのサブクラスインスタンスにコピーすることです。コピーメソッドでプロパティを指定する必要はありません。です。。この回答の一部は、この質問に対する他の回答に基づいていますが、必要な機能へのより明確なインターフェースを提供します。

ヘッダ:

//  SYNUtilities.h

#import <Foundation/Foundation.h>

@interface SYNUtilities : NSObject
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
@end

実装:

//  SYNUtilities.m

#import "SYNUtilities.h"
#import <objc/objc-runtime.h>

@implementation SYNUtilities
+ (NSDictionary *) propertiesOfObject:(id)object
{
    Class class = [object class];
    return [self propertiesOfClass:class];
}

+ (NSDictionary *) propertiesOfClass:(Class)class
{
    NSMutableDictionary * properties = [NSMutableDictionary dictionary];
    [self propertiesForHierarchyOfClass:class onDictionary:properties];
    return [NSDictionary dictionaryWithDictionary:properties];
}

+ (NSDictionary *) propertiesOfSubclass:(Class)class
{
    if (class == NULL) {
        return nil;
    }

    NSMutableDictionary *properties = [NSMutableDictionary dictionary];
    return [self propertiesForSubclass:class onDictionary:properties];
}

+ (NSMutableDictionary *)propertiesForHierarchyOfClass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
    if (class == NULL) {
        return nil;
    }

    if (class == [NSObject class]) {
        // On reaching the NSObject base class, return all properties collected.
        return properties;
    }

    // Collect properties from the current class.
    [self propertiesForSubclass:class onDictionary:properties];

    // Collect properties from the superclass.
    return [self propertiesForHierarchyOfClass:[class superclass] onDictionary:properties];
}

+ (NSMutableDictionary *) propertiesForSubclass:(Class)class onDictionary:(NSMutableDictionary *)properties
{
    unsigned int outCount, i;
    objc_property_t *objcProperties = class_copyPropertyList(class, &outCount);
    for (i = 0; i < outCount; i++) {
        objc_property_t property = objcProperties[i];
        const char *propName = property_getName(property);
        if(propName) {
            const char *propType = getPropertyType(property);
            NSString *propertyName = [NSString stringWithUTF8String:propName];
            NSString *propertyType = [NSString stringWithUTF8String:propType];
            [properties setObject:propertyType forKey:propertyName];
        }
    }
    free(objcProperties);

    return properties;
}

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // A C primitive type:
            /*
             For example, int "i", long "l", unsigned "I", struct.
             Apple docs list plenty of examples of values returned. For a list
             of what will be returned for these primitives, search online for
             "Objective-c" "Property Attribute Description Examples"
             */
            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // An Objective C id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // Another Objective C id type:
            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
    }
    return "";
}

@end

この行でEXC_BAD_ACCESS例外が発生しますNSString * name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute)-1 encoding:NSASCIIStringEncoding];
アダムメンドーサ

4

(私がしたように)親クラスから継承されたプロパティも取得する必要がある場合は、再帰的にするための" orange80 "コードにいくつかの変更を加えます。

+ (NSDictionary *)classPropsForClassHierarchy:(Class)klass onDictionary:(NSMutableDictionary *)results
{
    if (klass == NULL) {
        return nil;
    }

    //stop if we reach the NSObject class as is the base class
    if (klass == [NSObject class]) {
        return [NSDictionary dictionaryWithDictionary:results];
    }
    else{

        unsigned int outCount, i;
        objc_property_t *properties = class_copyPropertyList(klass, &outCount);
        for (i = 0; i < outCount; i++) {
            objc_property_t property = properties[i];
            const char *propName = property_getName(property);
            if(propName) {
                const char *propType = getPropertyType(property);
                NSString *propertyName = [NSString stringWithUTF8String:propName];
                NSString *propertyType = [NSString stringWithUTF8String:propType];
                [results setObject:propertyType forKey:propertyName];
            }
        }
        free(properties);

        //go for the superclass
        return [PropertyUtil classPropsForClassHierarchy:[klass superclass] onDictionary:results];

    }
}

1
これをカテゴリにしてNSObjectを拡張し、NSObjectの子であるすべてのクラスにこの機能を組み込むことはできませんか?
Alex Zavatone 2013

時間を見つけることができれば、そのオプションで回答が更新されます。
PakitoV 2013

それが終わったら、時間があるときにメソッドダンプを追加します。すべてのNSObjectの上に実際のオブジェクトプロパティとメソッドイントロスペクションを取得するときが来ました。
Alex Zavatone 2013

値出力の追加にも取り組んでいますが、一部の構造(rects)では、型はプロパティの実際の値であるようです。これは、tableViewControllerのcaretRectと、viewController構造体の他のunsigned intの場合で、objective-Cランタイムドキュメントと競合するタイプとしてcまたはfを返します。これを完成させるには、明らかにさらに多くの作業が必要です。 developer.apple.com/library/mac/documentation/cocoa/conceptual/…
アレックスZavatone

私は見ていましたが、回避できない問題があります。再帰的にするには、スーパークラスのメソッドを呼び出す必要があります(前のコードの最後の行のように)NSObjectはカテゴリ内で機能しないルートクラスであるため。したがって、再帰性はありません...:(NSObjectのカテゴリがもう進むべき道かどうかは
わかり

3

「属性」という言葉は少しあいまいです。アクセサーのように見えるインスタンス変数、プロパティ、メソッドを意味しますか?

3つすべてに対する答えは「はい、しかしそれは非常に簡単ではありません」です。Objective-CのランタイムAPIは、クラスのIVARリスト、メソッドのリストまたはプロパティリストを取得する機能を含む(例えば、class_copyPropertyList())、および各種類に対応する機能は、リスト内の項目の名前を取得する(例えば、property_getName())。

全体として、それを正しくするのは、たいへんな作業である場合もあれば、少なくとも、たいていの人がたいていの場合ほんのささいな機能にしたいことよりもはるかに多くの作業である場合もあります。

または、ヘッダーファイルを読み取り、クラスの「属性」と見なすものをすべて検索するRuby / Pythonスクリプトを作成することもできます。


こんにちはチャック、あなたの応答をありがとう。「属性」で私が言及していたのは、確かにクラスのプロパティです。Obj-Cランタイムライブラリを利用することで、私は自分のやりたいことをなんとか成し遂げました。スクリプトを使用してヘッダーファイルを解析しても、実行時に必要な機能を果たせなかったでしょう。
ボリーバ2009

3

@ orange80の答えをARC ENABLED WITH ARCで動作させることができました …...私が欲しかったもののために-少なくとも...少し試行錯誤せずにではありませんでした。うまくいけば、この追加情報が誰かの悲しみを救うかもしれません。

彼が彼の回答で説明するクラスを =クラスとして保存し、あなたのAppDelegate.h(または何でも)に入れてください#import PropertyUtil.h。次にあなたの...

- (void)applicationDidFinishLaunching:
         (NSNotification *)aNotification {

メソッド(または何でも)

PropertyUtil *props  = [PropertyUtil new];  
NSDictionary *propsD = [PropertyUtil classPropsFor:
                          (NSObject*)[gist class]];  
NSLog(@"%@, %@", props, propsD);

秘密は、クエリしたいクラスのインスタンス変数をキャストすることです(この場合、私のクラスはGistであり、私のインスタンスはGistですgist)... NSObject(id)などへのキャストそれをカットしません..さまざまな奇妙な、難解な理由。これにより、次のような出力が得られます…

<PropertyUtil: 0x7ff0ea92fd90>, {
apiURL = NSURL;
createdAt = NSDate;
files = NSArray;
gistDescription = NSString;
gistId = NSString;
gitPullURL = NSURL;
gitPushURL = NSURL;
htmlURL = NSURL;
isFork = c;
isPublic = c;
numberOfComments = Q;
updatedAt = NSDate;
userLogin = NSString;
}

AppleがObjCの「amazeballs」「内省」について自慢している/ OCDを自慢しているすべての人にとって、彼らはこの単純な「見た目」「自分の目で」、「いわば」を実行することを非常に簡単にしません。

でも、もしあなたが本当に独り占めしたいなら..チェックアウト.. class-dumpは、実行ファイルのクラスヘッダーをのぞき見するのに気が遠くなるようなめちゃくちゃな方法です…それはあなたのクラスを詳細に調べます...個人的に、本当に役立つ-多くの、多くの状況で。それが、私がOPの質問に対する解決策を模索し始めた理由です。ここにいくつかの使用パラメータがあります。

    -a             show instance variable offsets
    -A             show implementation addresses
    --arch <arch>  choose a specific architecture from a universal binary (ppc, ppc64, i386, x86_64)
    -C <regex>     only display classes matching regular expression
    -f <str>       find string in method name
    -I             sort classes, categories, and protocols by inheritance (overrides -s)
    -r             recursively expand frameworks and fixed VM shared libraries
    -s             sort classes and categories by name
    -S             sort methods by name

3

あなたは3つの魔法を持っています

Ivar* ivars = class_copyIvarList(clazz, &count); // to get all iVars
objc_property_t  *properties = class_copyPropertyList(clazz, &count); //to get all properties of a class 
Method* methods = class_copyMethodList(clazz, &count); // to get all methods of a class.

以下のコードが役立ちます。

-(void) displayClassInfo
{
    Class clazz = [self class];
    u_int count;

    Ivar* ivars = class_copyIvarList(clazz, &count);
    NSMutableArray* ivarArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* ivarName = ivar_getName(ivars[i]);
        ivarArray addObject:[NSString  stringWithCString:ivarName encoding:NSUTF8StringEncoding]];
    }
    free(ivars);

    objc_property_t* properties = class_copyPropertyList(clazz, &count);
    NSMutableArray* propertyArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        const char* propertyName = property_getName(properties[i]);
        [propertyArray addObject:[NSString  stringWithCString:propertyName encoding:NSUTF8StringEncoding]];
    }
    free(properties);

    Method* methods = class_copyMethodList(clazz, &count);
    NSMutableArray* methodArray = [NSMutableArray arrayWithCapacity:count];
    for (int i = 0; i < count ; i++)
    {
        SEL selector = method_getName(methods[i]);
        const char* methodName = sel_getName(selector);
        [methodArray addObject:[NSString  stringWithCString:methodName encoding:NSUTF8StringEncoding]];
    }
    free(methods);

    NSDictionary* classInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                           ivarArray, @"ivars",
                           propertyArray, @"properties",
                           methodArray, @"methods",
                           nil];

        NSLog(@"%@", classInfo);
}

2

私は提供されている関数bolivaを使用していましたが、明らかにiOS 7では機能しなくなりました。静的なconst char * getPropertyType(objc_property_t property)の代わりに、次のように使用できます。

- (NSString*) classOfProperty:(NSString*)propName{

objc_property_t prop = class_getProperty([self class], [propName UTF8String]);
if (!prop) {
    // doesn't exist for object
    return nil;
}
const char * propAttr = property_getAttributes(prop);
NSString *propString = [NSString stringWithUTF8String:propAttr];
NSArray *attrArray = [propString componentsSeparatedByString:@","];
NSString *class=[attrArray objectAtIndex:0];
return [[class stringByReplacingOccurrencesOfString:@"\"" withString:@""] stringByReplacingOccurrencesOfString:@"T@" withString:@""];
}

あなたは私のヒーローです。私はまだいくつかのことを手動で修正する必要があります(何らかの理由でBOOLが「Tc」として表示されます)が、これにより実際に物事を再び機能させることができました。
Harpastum 2013

プリミティブには独自のタイプがあり、「@」はオブジェクトを示し、その後にクラス名が引用符の間に表示されます。唯一の例外は、単に「T @」としてエンコードされているidです
Mihai Timar

2

Swiftの見物人は、この機能を利用してこの機能を取得できEncodableます。方法を説明します:

  1. オブジェクトをEncodableプロトコルに適合させる

    class ExampleObj: NSObject, Encodable {
        var prop1: String = ""
        var prop2: String = ""
    }
  2. 機能Encodableを提供するための拡張機能を作成するtoDictionary

     public func toDictionary() -> [String: AnyObject]? {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        guard let data =  try? encoder.encode(self),
              let json = try? JSONSerialization.jsonObject(with: data, options: .init(rawValue: 0)), let jsonDict = json as? [String: AnyObject] else {
            return nil
        }
        return jsonDict
    }
  3. toDictionaryオブジェクトインスタンスを呼び出し、keysプロパティにアクセスします。

    let exampleObj = ExampleObj()
    exampleObj.toDictionary()?.keys
  4. 出来上がり!次のようにプロパティにアクセスします。

    for k in exampleObj!.keys {
        print(k)
    }
    // Prints "prop1"
    // Prints "prop2"

1

これらの回答は役に立ちますが、それ以上のことが必要です。プロパティのクラスタイプが既存のオブジェクトのクラスタイプと等しいかどうかを確認するだけです。オブジェクトのクラス名を取得するために、object_getClassName()は次のようなテキストを返します。

__NSArrayI (for an NSArray instance)
__NSArrayM (for an NSMutableArray instance)
__NSCFBoolean (an NSNumber object initialized by initWithBool:)
__NSCFNumber (an NSValue object initialized by [NSNumber initWithBool:])

ただし、上記のサンプルコードからgetPropertyType(...)を呼び出す場合は、次のように定義されたクラスのプロパティの4つのobjc_property_t構造体を使用します。

@property (nonatomic, strong) NSArray* a0;
@property (nonatomic, strong) NSArray* a1;
@property (nonatomic, copy) NSNumber* n0;
@property (nonatomic, copy) NSValue* n1;

次のようにそれぞれ文字列を返します。

NSArray
NSArray
NSNumber
NSValue

したがって、NSObjectがクラスの1つのプロパティの値になることができるかどうかを判断することはできません。それをどうやって行うのですか?

これが私の完全なサンプルコードです(関数getPropertyType(...)は上記と同じです):

#import <objc/runtime.h>

@interface FOO : NSObject

@property (nonatomic, strong) NSArray* a0;
@property (nonatomic, strong) NSArray* a1;
@property (nonatomic, copy) NSNumber* n0;
@property (nonatomic, copy) NSValue* n1;

@end

@implementation FOO

@synthesize a0;
@synthesize a1;
@synthesize n0;
@synthesize n1;

@end

static const char *getPropertyType(objc_property_t property) {
    const char *attributes = property_getAttributes(property);
    //printf("attributes=%s\n", attributes);
    char buffer[1 + strlen(attributes)];
    strcpy(buffer, attributes);
    char *state = buffer, *attribute;
    while ((attribute = strsep(&state, ",")) != NULL) {
        if (attribute[0] == 'T' && attribute[1] != '@') {
            // it's a C primitive type:

            // if you want a list of what will be returned for these primitives, search online for
            // "objective-c" "Property Attribute Description Examples"
            // apple docs list plenty of examples of what you get for int "i", long "l", unsigned "I", struct, etc.

            NSString *name = [[NSString alloc] initWithBytes:attribute + 1 length:strlen(attribute) - 1 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
        else if (attribute[0] == 'T' && attribute[1] == '@' && strlen(attribute) == 2) {
            // it's an ObjC id type:
            return "id";
        }
        else if (attribute[0] == 'T' && attribute[1] == '@') {
            // it's another ObjC object type:
            NSString *name = [[NSString alloc] initWithBytes:attribute + 3 length:strlen(attribute) - 4 encoding:NSASCIIStringEncoding];
            return (const char *)[name cStringUsingEncoding:NSASCIIStringEncoding];
        }
    }
    return "";
}

int main(int argc, char * argv[]) {
    NSArray* a0 = [[NSArray alloc] init];
    NSMutableArray* a1 = [[NSMutableArray alloc] init];
    NSNumber* n0 = [[NSNumber alloc] initWithBool:YES];
    NSValue* n1 = [[NSNumber alloc] initWithBool:NO];
    const char* type0 = object_getClassName(a0);
    const char* type1 = object_getClassName(a1);
    const char* type2 = object_getClassName(n0);
    const char* type3 = object_getClassName(n1);

    objc_property_t property0 = class_getProperty(FOO.class, "a0");
    objc_property_t property1 = class_getProperty(FOO.class, "a1");
    objc_property_t property2 = class_getProperty(FOO.class, "n0");
    objc_property_t property3 = class_getProperty(FOO.class, "n1");
    const char * memberthype0 = getPropertyType(property0);//property_getAttributes(property0);
    const char * memberthype1 = getPropertyType(property1);//property_getAttributes(property1);
    const char * memberthype2 = getPropertyType(property2);//property_getAttributes(property0);
    const char * memberthype3 = getPropertyType(property3);//property_getAttributes(property1);
    NSLog(@"%s", type0);
    NSLog(@"%s", type1);
    NSLog(@"%s", type2);
    NSLog(@"%s", type3);
    NSLog(@"%s", memberthype0);
    NSLog(@"%s", memberthype1);
    NSLog(@"%s", memberthype2);
    NSLog(@"%s", memberthype3);

    return 0;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.