Objective-C'de bir nesne özellikleri listesi alın


109

Objective-C'de belirli bir nesne özelliklerinin bir listesini ( NSArrayveya biçiminde NSDictionary) nasıl alabilirim ?

Şu senaryoyu hayal edin: Sadece genişleten NSObject, bir NSString, a BOOLve bir NSDatanesneyi özellikler olarak tutan bir ana sınıf tanımladım . Sonra, bu ebeveyn sınıfı genişleten ve her biri birçok farklı özellik ekleyen birkaç sınıfım var.

Ben bir örnek yöntemini uygulamak herhangi bir yolu var mı ebeveyn bütün nesne ve iadeler, söz hakkından, bir geçer sınıfın NSArraysınıf özelliklerine (çocuğa) her birinin olarak NSStringso vardır değil daha sonra bu kullanabilmesi için, ana sınıfına NSStringKVC için?

Yanıtlar:


116

Cevabı kendim almayı başardım. Obj-C Çalışma Zamanı Kitaplığı'nı kullanarak özelliklere istediğim şekilde erişebildim:

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

Bu, temelde bir Apple kod örneğinden alınan bir 'getPropertyType' C işlevi yapmamı gerektirdi (şu anda tam kaynağı hatırlayamıyorum):

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
Bunun dışında +1, int gibi ilkellerde hata verecektir. Aynı şeyin biraz geliştirilmiş versiyonu için lütfen aşağıdaki cevabıma bakın.
jpswain

1
Doğruluk gereği, [NSString stringWithCString:]lehine kullanımdan kaldırılmıştır [NSString stringWithCString:encoding:].
zekel

4
Objc çalışma zamanı başlığını içe aktarmalıdır #import <objc / runtime.h> ARC üzerinde çalışır.
Dae KIM

Swift kullanarak bunu nasıl başaracağınız aşağıda açıklanmıştır .
Ramis

76

@ boliva'nın cevabı iyi, ancak int, long, float, double vb. gibi ilkelleri işlemek için biraz daha fazlasına ihtiyaç var.

Bu işlevi eklemek için ondan yola çıktım.

// 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 dosyasının en üstünde olmasını mı düşündünüz ?
Andrew

2
[NSString stringWithUTF8String: propType], "propType const char *" NSNumber \ x94 \ xfdk; "öğesini ayrıştıramadı ve bir nil dizesi döndürdü ... Bunun neden bu kadar garip bir NSNumber olduğunu bilmiyorum. Mb, çünkü ActiveRecord?
Dumoko

Süper! Çok teşekkürler.
Azik Abdullah

Bu kesinlikle mükemmel!
Pranoy C

28

@ orange80'in cevabının bir sorunu var: Aslında dizeyi her zaman 0'larla sonlandırmaz. Bu, UTF8'e dönüştürmeye çalışırken çökme gibi beklenmedik sonuçlara yol açabilir (aslında sırf bu yüzden oldukça can sıkıcı bir hata ayıklama yaşadım. Hata ayıklamak eğlenceliydi ^^). Aslında öznitelikten bir NSString alarak ve ardından cStringUsingEncoding: 'i çağırarak bunu düzelttim. Bu artık bir cazibe gibi çalışıyor. (ARC ile de çalışır, en azından benim için)

Şimdi bu benim kod versiyonum:

// 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, verdiğim kodla ilgili sorunu gösteren bir örnek verebilir misiniz? sadece görmek için merak ediyorum.
jpswain

@ orange80 Peki, AFAIR veriler hiçbir zaman sıfır sonlandırılmaz. Eğer bu sadece kazayla olur. Yine de yanılıyor olabilirim. Diğer haberler: Bu kod hala çalışıyor ve çok sağlam çalışıyor: p
felinira

@ orange80 Google'ın IMA reklam kitaplığından IMAAdRequest'teki sürümünüzü çağırmaya çalışırken bu sorunla karşılaştım. farthen'in çözümü bunu çözdü.
Christopher Pickslay

Teşekkürler. Bu, önceki iki cevap olmadığında iOS7'de benim için çalıştı. Hepsi için +1 3.
ChrisH

Benim için işe yarayan tek cevap bu. Diğer her şey bana mülk türleri için "NSString \ x8d \ xc0 \ xd9" tuhaflığı veriyordu, çünkü muhtemelen char * boyutlandırması kapalıydı
Brian Colavito

8

İOS 3.2 ile denediğimde getPropertyType işlevi özellik açıklamasıyla iyi çalışmıyor. İOS belgelerinden bir örnek buldum: "Objective-C Runtime Programming Guide: Declared Properties".

İOS 3.2'de mülk listelemesi için revize edilmiş bir kod:

#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

Boliva'nın çözümünün simülatörde iyi çalıştığını, ancak cihazda sabit uzunluktaki alt dizenin sorunlara neden olduğunu buldum. Cihazda çalışan bu soruna daha Objective-C dostu bir çözüm yazdım. Sürümümde, özniteliklerin C-String'ini bir NSString'e dönüştürüyorum ve sadece tür açıklamasının bir alt dizesini elde etmek için dize işlemleri gerçekleştiriyorum.

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

Bu, NSRange const typeRangeStart = [öznitelikler rangeOfString: @ "T @ \" "]; // tür dizesinin başlangıcı
Adam Mendoza

6

Bu uygulama hem Objective-C nesne türleri hem de C ilkelleri ile çalışır. İOS 8 uyumludur. Bu sınıf, üç sınıf yöntemi sağlar:

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

Tüm üst sınıflarından olanlar da dahil olmak üzere bir nesnenin tüm görünür özelliklerinin sözlüğünü döndürür.

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

Bir sınıfın tüm üst sınıflarından olanlar dahil tüm görünür özelliklerinin sözlüğünü döndürür.

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

Bir alt sınıfa özgü tüm görünür özelliklerin sözlüğünü döndürür . Üst sınıflarının özellikleri dahil edilmemiştir .

Bu yöntemlerin kullanımının yararlı bir örneği, bir kopyalama yönteminde özellikleri belirtmek zorunda kalmadan bir nesneyi Objective-C'de bir alt sınıf örneğine kopyalamaktır . Bu cevabın bazı bölümleri bu soruya verilen diğer cevaplara dayanmaktadır, ancak istenen işlevsellik için daha temiz bir arayüz sağlar.

Başlık:

//  SYNUtilities.h

#import <Foundation/Foundation.h>

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

Uygulama:

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

Bu satırda bir EXC_BAD_ACCESS istisnası alıyorum NSString * name = [[NSString ayırma] initWithBytes: özellik + 1 uzunluk: strlen (özellik) - 1 kodlama: NSASCIIStringEncoding];
Adam Mendoza

4

Eğer birisi üst sınıflardan miras alınan özellikleri de almaya ihtiyaç duyuyorsa (benim yaptığım gibi) burada " orange80 " kodunda onu tekrarlamalı hale getirmek için bazı değişiklikler var :

+ (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
Bunu bir kategori haline getirip NSObject'i bununla genişletemez miyiz, böylece bu işlevsellik NSObject'in alt öğesi olan her sınıfta yerleşik olsun?
Alex Zavatone

Bu iyi bir fikir gibi geliyor, eğer zamanı bulabilirsem cevabı bu seçenekle güncelleyeceğim.
PakitoV

Bununla işiniz bittiğinde, zamanım olduğunda bir yöntem dökümü ekleyeceğim. Her NSObject'in üzerine gerçek nesne özelliği ve yöntem iç gözlemi yapmamızın zamanı geldi.
Alex Zavatone

Değer çıktısı eklemek için de çalışıyorum, ancak bazı yapılar (rects) için tipin mülkün gerçek değeri olduğu görülüyor. Bu, bir tableViewController'ın caretRect'i ve bir viewController struct yapısındaki diğer imzalanmamış ints'lerde, amaç-C Runtime belgeleri ile çakışan tür olarak c veya f döndürür. Bunu tamamlamak için burada açıkça daha fazla çalışmaya ihtiyaç var. developer.apple.com/library/mac/documentation/cocoa/conceptual/…
Alex Zavatone

Bir göz atıyordum ama geçici çözümleyemediğim bir sorun var, bunu yinelemeli yapmak için NSObject bir kategori içinde çalışmayan kök sınıf olduğu için süper sınıf için yöntemi (önceki kodun son satırındaki gibi) çağırmam gerekiyor . Yani yineleme mümkün değil ... :( NSObject'teki bir kategorinin artık gidilecek yol olup olmadığından emin değilim ...
PakitoV

3

"Nitelikler" kelimesi biraz belirsizdir. Erişimci gibi görünen örnek değişkenleri, özellikleri, yöntemleri mi kastediyorsunuz?

Üçünün de cevabı "evet, ama çok kolay değil." Amaç-C çalışma zamanı API bir sınıfa ivar listesi, yöntem listesi veya özellik listesine işlevleri içerir (örneğin, class_copyPropertyList()), ve sonra da her bir türü için karşılık gelen bir işlevi listedeki bir ürünün adı almak için (örneğin, property_getName()).

Sonuç olarak, doğru yapmak çok fazla iş olabilir veya en azından çoğu insanın gerçekten önemsiz bir özellik anlamına gelen şey için yapmak isteyeceğinden çok daha fazla olabilir.

Alternatif olarak, sadece bir başlık dosyasını okuyan ve sınıf için "öznitelikler" olarak düşündüğünüz her şeyi arayan bir Ruby / Python betiği yazabilirsiniz.


Merhaba Chuck, cevabın için teşekkürler. 'Öznitelikler' ile bahsettiğim şey, aslında bir sınıf özellikleriyle ilgiliydi. Obj-C Çalışma Zamanı Kitaplığı'nı kullanarak istediğimi gerçekleştirmeyi zaten başardım. Başlık dosyasını ayrıştırmak için bir komut dosyası kullanmak, çalışma zamanında ihtiyacım olan şey için işe yaramazdı.
boliva

3

@ Orange80'in cevabını ARK ETKİNLEŞTİRİLDİ ... ... istediğim şey için alabildim - en azından ... ama biraz deneme yanılma olmadan. Umarım bu ek bilgi birisini kederden kurtarabilir.

Cevabında tanımladığı sınıfları kaydedin = bir sınıf olarak ve sizin AppDelegate.h(veya her neyse) koyun #import PropertyUtil.h. Sonra senin ...

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

yöntem (veya her neyse)

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

Gizli Sınıfın örnek değişkeni döküm etmektir ( benim sınıftır bu Durumunda Gistve benim örnek Gistolduğunugist sen ... sorguya istediğiniz) NSObject için ... (id)çeşitli, garip için .., vb kesmeyecek , ezoterik nedenler. Bu size bazı çıktılar verecek ...

<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'ın ObjC'nin "hayranlık uyandıran" "iç gözlemi" hakkında övünen tüm utanmaz / OKB'si için ... Bu basit "bakış" "kişinin kendine", "tabiri caizse" gerçekleştirmesini kesinlikle kolaylaştırmıyorlar ..

Yine de gerçekten vahşi olmak istiyorsanız .. kontrol edin .. HERHANGİ bir yürütülebilir dosyanın sınıf başlıklarına göz atmak için akıllara durgunluk veren çılgın bir yol olan sınıf dökümü , vb… Sınıflarınıza SÖZEL bir bakış sağlar… ben, kişisel olarak, birçok durumda gerçekten yararlı bul. aslında bu yüzden OP'nin sorusuna bir çözüm aramaya başladım. İşte bazı kullanım parametreleri .. Keyfini çıkarın!

    -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

Üç sihirli büyün var

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.

Aşağıdaki kod parçası size yardımcı olabilir.

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

Sağlanan boliva işlevini kullanıyordum, ancak görünüşe göre iOS 7 ile çalışmayı bıraktı. Yani şimdi statik const char * getPropertyType (objc_property_t özelliği) yerine aşağıdakileri kullanabilirsiniz:

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

Sen benim kahramanımsın. Hala bazı şeyleri manuel olarak düzeltmem gerekiyor (Bazı nedenlerden dolayı BOOL'lar 'Tc' olarak geliyor), ancak bu aslında işleri tekrar çalıştırmamı sağladı.
Harpastum

İlkellerin kendi türleri vardır, "@" nesneleri belirtir ve ondan sonra sınıf adı tırnak işaretleri arasında görünür. Tek istisna, basitçe "T @" olarak kodlanan id
Mihai Timar

2

Swift izleyicileri için, bu işlevselliği, Encodable . Nasıl olduğunu açıklayacağım:

  1. Nesnenizi Encodableprotokole uygun hale getirin

    class ExampleObj: NSObject, Encodable {
        var prop1: String = ""
        var prop2: String = ""
    }
  2. İşlevsellik Encodablesağlamak için uzantı oluşturuntoDictionary

     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. toDictionaryNesne örneğinizi ve erişim keysözelliğinizi çağırın .

    let exampleObj = ExampleObj()
    exampleObj.toDictionary()?.keys
  4. İşte bu kadar! Gayrimenkullerinize şu şekilde erişin:

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

1

Bu cevaplar faydalıdır, ancak bundan daha fazlasını istiyorum. Tek yapmak istediğim, bir özelliğin sınıf türünün mevcut bir nesneninkine eşit olup olmadığını kontrol etmek. Yukarıdaki tüm kodlar bunu yapamaz çünkü: Bir nesnenin sınıf adını almak için, object_getClassName () şuna benzer metinler döndürür:

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

Ancak yukarıdaki örnek koddan getPropertyType (...) çağrısı yapılıyorsa, aşağıdaki gibi tanımlanmış bir sınıfın özelliklerinin 4 objc_property_t yapısı ile:

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

sırayla aşağıdaki gibi dizeleri döndürür:

NSArray
NSArray
NSNumber
NSValue

Dolayısıyla, bir NSObject öğesinin sınıfın bir özelliğinin değeri olup olmadığını belirleyemez. O zaman bunu nasıl yapmalı?

İşte tam örnek kodum (getPropertyType (...) işlevi yukarıdakiyle aynıdır):

#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;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.