Dapatkan daftar properti objek di Objective-C


109

Bagaimana saya bisa mendapatkan daftar (dalam bentuk NSArrayatau NSDictionary) dari properti objek tertentu di Objective-C?

Bayangkan skenario berikut: Saya telah mendefinisikan kelas induk yang hanya diperluas NSObject, yang menampung NSString, a, BOOLdan NSDataobjek sebagai properti. Lalu saya memiliki beberapa kelas yang memperluas kelas induk ini, menambahkan banyak properti yang berbeda masing-masing.

Apakah ada cara saya bisa mengimplementasikan metode instance pada kelas induk yang melewati seluruh objek dan mengembalikan, katakanlah, NSArraydari setiap properti kelas (anak) seperti NSStringsyang tidak ada di kelas induk, jadi saya nanti bisa menggunakan ini NSStringuntuk KVC?

Jawaban:


116

Saya baru saja berhasil mendapatkan jawabannya sendiri. Dengan menggunakan Obj-C Runtime Library, saya memiliki akses ke properti seperti yang saya inginkan:

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

Ini mengharuskan saya untuk membuat fungsi 'getPropertyType' C, yang sebagian besar diambil dari sampel kode Apple (saat ini tidak dapat mengingat sumber persisnya):

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 kecuali ini akan kesalahan pada primitif, seperti int. Silakan lihat jawaban saya di bawah untuk versi yang sedikit ditingkatkan dari hal yang sama ini.
jpswain

1
Sebagai soal kebenaran, [NSString stringWithCString:]sudah usang demi [NSString stringWithCString:encoding:].
zekel

4
Haruskah mengimpor object runtime header #import <objc / runtime.h> Ia bekerja pada ARC.
Dae KIM

Inilah cara melakukannya dengan menggunakan Swift.
Ramis

76

Jawaban @ boliva bagus, tetapi perlu sedikit ekstra untuk menangani primitif, seperti int, long, float, double, dll.

Saya membangunnya untuk menambahkan fungsi ini.

// 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
Apakah Anda bermaksud untuk memiliki #import <Foundation/Foundation.h>di bagian atas file .h?
Andrew

2
[NSString stringWithUTF8String: propType] tidak dapat mengurai "propType const char *" NSNumber \ x94 \ xfdk; "dan mengembalikan string nil ... Tidak tahu mengapa ini NSNumber aneh. Mb karena ActiveRecord?
Dumoko

Hebat! Terima kasih banyak.
Azik Abdullah

Ini benar-benar sempurna!
Pranoy C

28

Jawaban @ orange80 memiliki satu masalah: Ini sebenarnya tidak selalu mengakhiri string dengan 0s. Hal ini dapat menyebabkan hasil yang tidak terduga seperti crash saat mencoba mengubahnya menjadi UTF8 (Saya sebenarnya mengalami crashbug yang cukup mengganggu hanya karena itu. Sangat menyenangkan men-debug ^^). Saya memperbaikinya dengan benar-benar mendapatkan NSString dari atribut dan kemudian memanggil cStringUsingEncoding :. Ini bekerja seperti pesona sekarang. (Juga bekerja dengan ARC, setidaknya untuk saya)

Jadi ini versi kode saya sekarang:

// 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 dapatkah Anda memberikan contoh yang menunjukkan masalah dengan kode yang saya berikan? Saya hanya penasaran untuk melihatnya.
jpswain

@ orange80 Nah, AFAIR data tidak pernah nol sama sekali. Kalau sudah begini hanya terjadi secara kebetulan. Saya mungkin salah. Dalam berita lain: Saya masih menjalankan kode ini dan berjalan dengan kokoh: p
felinira

@ orange80 Saya mengalami masalah ini saat mencoba menjalankan versi Anda di IMAAdRequest dari perpustakaan iklan IMA Google. solusi farthen menyelesaikannya.
Christopher Pickslay

Terima kasih. Ini berhasil untuk saya di iOS7 ketika dua jawaban sebelumnya tidak. +1 untuk semua 3.
ChrisH

Ini adalah satu-satunya jawaban yang berhasil untuk saya. Segala sesuatu yang lain memberi saya keanehan "NSString \ x8d \ xc0 \ xd9" untuk jenis properti, mungkin karena ukuran karakter tidak aktif
Brian Colavito

8

Ketika saya mencoba dengan iOS 3.2, fungsi getPropertyType tidak bekerja dengan baik dengan deskripsi properti. Saya menemukan contoh dari dokumentasi iOS: "Objective-C Runtime Programming Guide: Declared Properties".

Berikut adalah kode yang direvisi untuk listing properti di 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

Saya telah menemukan bahwa solusi boliva berfungsi dengan baik di simulator, tetapi pada perangkat, substring dengan panjang tetap menyebabkan masalah. Saya telah menulis solusi yang lebih ramah Objective-C untuk masalah ini yang berfungsi pada perangkat. Dalam versi saya, saya mengonversi C-String dari atribut ke NSString dan melakukan operasi string di atasnya untuk mendapatkan substring dari deskripsi tipe saja.

/*
 * @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;
}

Ini melempar pengecualian EXC_BAD_ACCESS pada NSRange const typeRangeStart = [atribut rangeOfString: @ "T @ \" "]; // awal tipe string
Adam Mendoza

6

Implementasi ini bekerja dengan tipe objek Objective-C dan primitif C. Ini kompatibel dengan iOS 8. Kelas ini menyediakan tiga metode kelas:

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

Mengembalikan kamus dari semua properti objek yang terlihat, termasuk yang dari semua kelas supernya.

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

Mengembalikan kamus dari semua properti kelas yang terlihat, termasuk yang dari semua kelas supernya.

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

Mengembalikan kamus dari semua properti yang terlihat yang spesifik untuk subkelas. Properti untuk superclass-nya adalah tidak disertakan.

Salah satu contoh yang berguna dari penggunaan metode ini adalah dengan menyalin objek ke instance subclass di Objective-C tanpa harus menentukan properti dalam metode salin . Sebagian dari jawaban ini didasarkan pada jawaban lain untuk pertanyaan ini, tetapi memberikan antarmuka yang lebih bersih ke fungsionalitas yang diinginkan.

Header:

//  SYNUtilities.h

#import <Foundation/Foundation.h>

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

Penerapan:

//  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

Saya mendapatkan pengecualian EXC_BAD_ACCESS pada baris ini NSString * name = [[NSString alokasi] initWithBytes: atribut + 1 panjang: strlen (atribut) - 1 pengkodean: NSASCIIStringEncoding];
Adam Mendoza

4

Jika seseorang juga perlu mendapatkan properti yang diwarisi dari kelas induk (seperti yang saya lakukan) berikut ini beberapa modifikasi pada kode " orange80 " untuk membuatnya rekursif:

+ (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
Tidak bisakah kita membuat kategori ini dan memperluas NSObject dengannya sehingga fungsionalitas ini dibangun ke dalam setiap kelas yang merupakan anak dari NSObject?
Alex Zavatone

Kedengarannya ide yang bagus, jika saya dapat menemukan waktu akan memperbarui jawaban dengan opsi itu.
PakitoV

Setelah Anda selesai dengan itu, saya akan menambahkan metode dump ketika saya punya waktu. Sudah waktunya kita mendapatkan properti objek nyata dan introspeksi metode di atas setiap NSObject.
Alex Zavatone

Saya telah bekerja untuk menambahkan keluaran nilai juga, tetapi tampaknya untuk beberapa struktur (rects), tipenya adalah nilai aktual properti. Ini adalah kasus dengan caretRect dari tableViewController dan int unsigned lainnya dalam struct viewController return c atau f sebagai tipe yang bertentangan dengan dokumen Runtime Objective-C. Jelas lebih banyak pekerjaan diperlukan di sini untuk menyelesaikan ini. developer.apple.com/library/mac/documentation/cocoa/conceptual/…
Alex Zavatone

Saya sedang melihat-lihat tetapi ada masalah yang tidak dapat saya atasi, untuk membuatnya rekursif saya perlu memanggil metode untuk superclass (seperti pada baris terakhir dari kode sebelumnya) karena NSObject adalah kelas root yang tidak akan berfungsi di dalam kategori . Jadi tidak mungkin rekursif ... :( tidak yakin apakah kategori di NSObject adalah cara yang tepat lagi ...
PakitoV

3

Kata "atribut" agak kabur. Apakah yang Anda maksud adalah variabel instan, properti, metode yang terlihat seperti pengakses?

Jawaban dari ketiganya adalah "ya, tapi itu tidak mudah." The Objective-C runtime API termasuk fungsi untuk mendapatkan daftar Ivar, daftar metode atau daftar properti untuk kelas (misalnya, class_copyPropertyList()), dan kemudian sesuai fungsi untuk setiap jenis untuk mendapatkan nama item dalam daftar (misalnya, property_getName()).

Secara keseluruhan, ini bisa menjadi pekerjaan yang berat untuk memperbaikinya, atau setidaknya lebih banyak dari yang kebanyakan orang ingin lakukan untuk apa yang biasanya merupakan fitur yang sangat sepele.

Alternatifnya, Anda bisa menulis skrip Ruby / Python yang hanya membaca file header dan mencari apa pun yang Anda anggap "atribut" untuk kelas.


Halo chuck, terima kasih atas tanggapan Anda. Apa yang saya maksud dengan 'atribut' adalah properti kelas. Saya sudah berhasil mencapai apa yang saya inginkan dengan memanfaatkan Perpustakaan Obj-C Runtime. Menggunakan skrip untuk mengurai file header tidak akan berfungsi untuk apa yang saya butuhkan saat runtime.
boliva

3

Saya bisa mendapatkan jawaban @ orange80 untuk bekerja DENGAN ARC DIAKTIFKAN … ... untuk apa yang saya inginkan - setidaknya ... tetapi bukan tanpa sedikit trial and error. Semoga informasi tambahan ini dapat menghindarkan seseorang dari kesedihan.

Simpan kelas-kelas yang dia gambarkan dalam jawabannya = sebagai kelas, dan dalam Anda AppDelegate.h(atau apa pun), put #import PropertyUtil.h. Kemudian di ...

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

metode (atau apapun)

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

Rahasianya adalah untuk memasukkan variabel instan kelas Anda ( dalam hal ini kelas saya Gist, dan contoh saya Gistadalahgist ) yang ingin Anda tanyakan ... ke NSObject ... (id), dll, tidak akan memotongnya .. untuk berbagai, aneh , alasan esoteris. Ini akan memberi Anda beberapa keluaran seperti ...

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

Untuk semua Apple yang tidak tahu malu / OCD yang membual tentang introspeksi "amazeballs" "ObjC ... Mereka pasti tidak membuatnya sangat mudah untuk melakukan" tampilan "sederhana" pada diri sendiri "," boleh dikatakan "..

Jika Anda benar-benar ingin menjadi sangat liar .. periksa .. class-dump , yang merupakan cara yang sangat gila untuk mengintip header kelas dari SETIAP yang dapat dieksekusi, dll ... Ini memberikan tampilan VERBOSE ke kelas Anda ... bahwa saya, secara pribadi, menemukan yang benar-benar membantu - dalam banyak, banyak keadaan. itu sebenarnya mengapa saya mulai mencari solusi untuk pertanyaan OP. berikut adalah beberapa parameter penggunaan .. selamat menikmati!

    -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

Anda memiliki tiga mantra ajaib

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.

Sepotong kode berikut dapat membantu Anda.

-(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

Saya menggunakan fungsi boliva yang disediakan, tetapi ternyata itu berhenti bekerja dengan iOS 7. Jadi sekarang alih-alih static const char * getPropertyType (properti objc_property_t) seseorang dapat menggunakan yang berikut ini:

- (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:@""];
}

Kamu adalah pahlawan ku. Saya masih harus mengoreksi beberapa hal secara manual (Untuk beberapa alasan BOOL muncul sebagai 'Tc'), tetapi ini sebenarnya memungkinkan saya untuk membuat semuanya berfungsi kembali.
Harpastum

Primitif memiliki tipenya sendiri, "@" menunjukkan objek dan setelahnya nama kelas muncul di antara tanda kutip. Satu-satunya pengecualian adalah id yang dikodekan hanya sebagai "T @"
Mihai Timar

2

Untuk penonton Swift, Anda bisa mendapatkan fungsionalitas ini dengan memanfaatkan Encodablefungsionalitas tersebut. Saya akan menjelaskan caranya:

  1. Sesuaikan objek Anda dengan Encodableprotokol

    class ExampleObj: NSObject, Encodable {
        var prop1: String = ""
        var prop2: String = ""
    }
  2. Buat ekstensi untuk Encodablemenyediakan toDictionaryfungsionalitas

     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. Panggil toDictionaryinstance objek Anda dan akses keysproperti.

    let exampleObj = ExampleObj()
    exampleObj.toDictionary()?.keys
  4. Voila! Akses properti Anda seperti ini:

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

1

Jawaban-jawaban ini sangat membantu, tetapi saya membutuhkan lebih dari itu. Yang ingin saya lakukan adalah memeriksa apakah tipe kelas suatu properti sama dengan objek yang ada. Semua kode di atas tidak mampu melakukannya, karena: Untuk mendapatkan nama kelas dari sebuah objek, object_getClassName () mengembalikan teks seperti ini:

__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:])

Tetapi jika memanggil getPropertyType (...) dari kode contoh di atas, dengan 4 struct_property_t objc dari properti kelas yang didefinisikan seperti ini:

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

itu mengembalikan string masing-masing sebagai berikut:

NSArray
NSArray
NSNumber
NSValue

Jadi tidak dapat menentukan apakah NSObject mampu menjadi nilai salah satu properti kelas. Lalu bagaimana cara melakukannya?

Berikut ini contoh kode lengkap saya (fungsi getPropertyType (...) sama seperti di atas):

#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;
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.