Objective-C에서 개체 속성 목록 가져 오기


109

Objective-C에서 주어진 객체 속성 의 목록 ( NSArray또는 형식)을 얻으려면 어떻게 NSDictionary해야합니까?

다음 시나리오를 상상해보십시오. , a 및 객체를 속성으로 NSObject보유하는을 확장하는 부모 클래스를 정의했습니다 . 그런 다음이 부모 클래스를 확장하는 여러 클래스가 있으며 각각 많은 속성을 추가합니다. NSStringBOOLNSData

나는에서 인스턴스 메서드 구현할 수있는 방법이 상위 전체 개체를 반환, 말, 통과 클래스 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

1
정확성의 문제로, [NSString stringWithCString:]대신 [NSString stringWithCString:encoding:].
zekel

4
objc 런타임 헤더를 가져와야합니다. #import <objc / runtime.h> ARC에서 작동합니다.
Dae KIM

Swift를 사용하여 수행하는 방법은 다음과 같습니다 .
Ramis

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; "를 구문 분석 할 수 없으며 nil 문자열을 반환합니다 ... 왜이게 이상한 NSNumber인지 모르겠습니다. Mb 때문에 ActiveRecord?
Dumoko

훌륭한! 감사합니다.
Azik Abdullah apr

이것은 절대적으로 완벽합니다!
Pranoy C

28

@ orange80의 대답에는 한 가지 문제가 있습니다. 실제로 항상 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 데이터는 전혀 0으로 끝나지 않습니다. 이 경우 우연히 발생합니다. 그래도 내가 틀릴 수 있습니다. 다른 뉴스에서 : 나는 아직도이 코드가 실행 중이 그것은 단단한 바위 실행 : P
felinira

@ orange80 Google의 IMA 광고 라이브러리에서 IMAAdRequest에서 버전을 호출하는 중에이 문제가 발생했습니다. farthen의 솔루션이 그것을 해결했습니다.
Christopher Pickslay 2014 년

감사. 이전 두 답변이 그렇지 않은 iOS7에서 이것은 나를 위해 일했습니다. 모든 3에 대해 +1
ChrisH 2014

이것은 나를 위해 일한 유일한 대답입니다. 다른 모든처럼 저를주고 있었다 "는 NSString \ x8d \ xc0 \ xd9"속성 유형에 대한 불확실성, 아마도 때문에 크기가 꺼져 * 숯불
브라이언 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

boliva의 솔루션은 시뮬레이터에서 잘 작동하지만 장치에서는 고정 길이 하위 문자열이 문제를 일으킨다는 것을 발견했습니다. 장치에서 작동하는이 문제에 대해보다 Objective-C 친화적 인 솔루션을 작성했습니다. 내 버전에서는 속성의 C-String을 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에 EXC_BAD_ACCESS 예외가 발생합니다. const typeRangeStart = [attributes rangeOfString : @ "T @ \" "]; // 문자열 유형 시작
Adam Mendoza

6

이 구현은 Objective-C 객체 유형 및 C 기본 요소 모두에서 작동합니다. iOS 8과 호환됩니다. 이 클래스는 세 가지 클래스 메서드를 제공합니다.

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

모든 슈퍼 클래스의 속성을 포함하여 객체의 모든 보이는 속성의 사전을 반환합니다.

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

모든 슈퍼 클래스의 속성을 포함하여 클래스의 모든 보이는 속성의 사전을 반환합니다.

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

하위 클래스에 특정한 모든 가시적 속성의 사전을 반환합니다 . 수퍼 클래스의 속성은 포함 되지 않습니다.

이러한 메서드를 사용하는 한 가지 유용한 예 는 복사 메서드에서 속성을 지정하지 않고도 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];
Adam Mendoza

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

이 작업이 끝나면 시간이있을 때 메소드 덤프를 추가하겠습니다. 이제 우리는 모든 NSObject 위에 실제 객체 속성과 메소드 검사를 할 때입니다.
Alex Zavatone

값 출력을 추가하는 작업도 해왔지만 일부 구조 (rects)의 경우 유형이 속성의 실제 값인 것처럼 보입니다. 이것은 tableViewController의 caretRect 및 viewController 구조체의 다른 서명되지 않은 int가 목적 C 런타임 문서와 충돌하는 유형으로 c 또는 f를 반환하는 경우입니다. 이 작업을 완료하려면 분명히 더 많은 작업이 필요합니다. developer.apple.com/library/mac/documentation/cocoa/conceptual/...
알렉스 Zavatone에게

살펴 보았지만 해결할 수없는 문제가 있습니다. 재귀 적으로 만들려면 NSObject가 범주 내에서 작동하지 않는 루트 클래스이므로 수퍼 클래스에 대한 메서드를 호출해야합니다 (이전 코드의 마지막 줄과 같음). . NSObject의의 범주가 ... 더 이상 갈 방법이 확실하지 경우 :( ... 아니 recursivity 수 그래서
PakitoV

3

"속성"이라는 단어는 약간 모호합니다. 접근 자처럼 보이는 인스턴스 변수, 속성, 메서드를 의미합니까?

세 가지 모두에 대한 대답은 "예,하지만 쉽지는 않습니다."입니다. 오브젝티브 C 런타임 API는 클래스에 대한 아이바리스트 방법 목록 또는 속성 목록을 얻는 기능을 포함한다 (예 class_copyPropertyList()), 다음 각 유형에 대응하는 기능 목록의 항목의 이름을 얻기 위해 (예를 들어, property_getName()).

대체로 제대로 작동하려면 많은 작업이 필요할 수도 있고, 일반적으로 매우 사소한 기능에 해당하는 대부분의 사람들이 원하는 것보다 훨씬 많은 작업이 될 수도 있습니다.

또는 헤더 파일을 읽고 클래스에 대해 "속성"으로 간주 할 항목을 찾는 Ruby / Python 스크립트를 작성할 수도 있습니다.


안녕하세요 척, 답변 해 주셔서 감사합니다. 내가 '속성'으로 언급 한 것은 실제로 클래스 속성에 관한 것입니다. Obj-C 런타임 라이브러리를 사용하여 내가 원하는 것을 이미 달성했습니다. 스크립트를 사용하여 헤더 파일을 구문 분석하면 런타임에 필요한 작업을 수행하지 못했을 것입니다.
boliva 2009

3

나는 ARC ENABLED 로 작업하기 위해 @ orange80의 대답을 얻을 수 있었다. ... ... 내가 원했던 것에 대해-적어도 ...하지만 약간의 시행 착오 없이는 아니었다. 이 추가 정보가 누군가의 슬픔을 덜어 줄 수 있기를 바랍니다.

그가 설명하는 클래스를 그의 대답 = 클래스로 저장 하고 당신의 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;
}

ObjC의 "아마 제볼" "내성"에 대한 Apple의 부끄럽지 않은 / OCD 자랑을 위해 ... 그들은 확실히이 간단한 "모습" "자신으로", "말하자면"을 수행하는 것을 매우 쉽게 만들지 않습니다.

만약 당신이 정말로 야생으로 가고 싶다면 .. check out .. 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

세 가지 마법 주문이 있습니다

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 년

프리미티브에는 고유 한 유형이 있으며 "@"는 객체를 나타내고 그 뒤에 클래스 이름이 따옴표 사이에 표시됩니다. 유일한 예외는 단순히 인코딩 ID 인 "T @"
미하이 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가 클래스의 한 속성 값이 될 수 있는지 여부를 결정할 수 없습니다. 그럼 어떻게할까요?

다음은 전체 샘플 코드입니다 (function 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.