[objective-c] उद्देश्य-सी में ऑब्जेक्ट गुण सूची प्राप्त करें


Answers

@ बोलिवा का जवाब अच्छा है, लेकिन प्राइमेटिव को संभालने के लिए थोड़ा अतिरिक्त जरूरत है, जैसे 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

Question

मैं उद्देश्य-सी में दिए गए ऑब्जेक्ट विशेषताओं की सूची ( NSArray या NSDictionary ) कैसे प्राप्त कर सकता हूं?

निम्नलिखित परिदृश्य की कल्पना करें: मैंने एक अभिभावक वर्ग को परिभाषित किया है जो केवल NSObject बढ़ाता है, जिसमें NSString , एक BOOL और NSData ऑब्जेक्ट विशेषता है। तब मेरे पास कई कक्षाएं हैं जो इस अभिभावक वर्ग को बढ़ाती हैं, जिनमें प्रत्येक के कई अलग-अलग गुण शामिल होते हैं।

क्या कोई तरीका है कि मैं अभिभावक वर्ग पर एक इंस्टेंस विधि को कार्यान्वित कर सकता हूं जो पूरे ऑब्जेक्ट के माध्यम से जाता है और कहता है, प्रत्येक (बच्चे) वर्ग विशेषताओं का NSStrings जो माता-पिता वर्ग पर नहीं है , इसलिए मैं बाद में कर सकता हूं NSString लिए इन NSString उपयोग करें?




जब मैंने आईओएस 3.2 के साथ प्रयास किया, तो GetPropertyType फ़ंक्शन संपत्ति विवरण के साथ अच्छी तरह से काम नहीं करता है। मुझे आईओएस दस्तावेज से एक उदाहरण मिला: "उद्देश्य-सी रनटाइम प्रोग्रामिंग गाइड: घोषित गुण"।

आईओएस 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);



अगर किसी को माता-पिता वर्गों (जैसे मैंने किया) से विरासत में प्राप्त संपत्तियों की आवश्यकता होती है, तो यहां इसे " 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];

    }
}



मैं फ़ंक्शन बोलिवा का उपयोग कर रहा था, लेकिन स्पष्ट रूप से यह आईओएस 7 के साथ काम करना बंद कर दिया। तो अब स्थिर कॉन्स char * getPropertyType (objc_property_t प्रॉपर्टी) के बजाय कोई भी निम्न का उपयोग कर सकता है:

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



यह कार्यान्वयन उद्देश्य-सी ऑब्जेक्ट प्रकार और सी प्राइमेटिव दोनों के साथ काम करता है। यह आईओएस 8 संगत है। यह वर्ग तीन वर्ग विधियों को प्रदान करता है:

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

ऑब्जेक्ट के सभी दृश्य गुणों का एक शब्दकोश देता है, जिसमें इसके सभी सुपरक्लास शामिल हैं।

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

कक्षा के सभी दृश्य गुणों का एक शब्दकोश देता है, जिसमें इसके सभी सुपरक्लास शामिल हैं।

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

सबक्लास के लिए विशिष्ट सभी दृश्य गुणों का एक शब्दकोश देता है। इसके सुपरक्लास के लिए गुण शामिल नहीं हैं।

इन विधियों के उपयोग का एक उपयोगी उदाहरण ऑब्जेक्टिव-सी में किसी ऑब्जेक्ट को प्रतिलिपि उदाहरण में गुणों को निर्दिष्ट करने के बिना किसी ऑब्जेक्ट को कॉपी करना है । इस उत्तर के भाग इस प्रश्न के अन्य उत्तरों पर आधारित हैं लेकिन यह वांछित कार्यक्षमता के लिए एक क्लीनर इंटरफ़ेस प्रदान करता है।

हैडर:

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



ये उत्तर उपयोगी हैं, लेकिन मुझे इससे अधिक आवश्यकता है। मैं बस यह जांचना चाहता हूं कि संपत्ति का वर्ग प्रकार किसी मौजूदा ऑब्जेक्ट के बराबर है या नहीं। उपर्युक्त सभी कोड ऐसा करने में सक्षम नहीं हैं, क्योंकि: किसी ऑब्जेक्ट का क्लास नाम प्राप्त करने के लिए, 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 (...) का आह्वान करते हैं, तो इस तरह परिभाषित कक्षा के गुणों के wit 4 objc_property_t structs:

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

यह क्रमशः क्रमशः स्ट्रिंग देता है:

NSArray
NSArray
NSNumber
NSValue

इसलिए यह निर्धारित करने में सक्षम नहीं है कि एनएसओब्जेक्ट कक्षा की एक संपत्ति का मूल्य होने में सक्षम है या नहीं। तब ऐसा कैसे करें?

यहां मेरा पूरा नमूना कोड है (फ़ंक्शन 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;
}



Links