30

有没有办法获得某种类属性的数组?例如,如果我有这样的界面

@interface MyClass : NSObject
    @property (strong,nonatomic) UILabel *firstLabel;
    @property (strong,nonatomic) UILabel *secondLabel;        
@end

我可以在不知道它们的名字的情况下获得对这些标签的引用吗?

@implementation MyClass
    -(NSArray*)getListOfAllLabels
    {
            ?????
    }        
@end

我知道我可以很容易地做到这一点[NSArray arrayWithObjects:firstLabel,secondLabel,nil],但我想用某种类枚举来做到这一点for (UILabel* oneLabel in ???[self objects]???)

4

6 回答 6

79

所以更准确地说,如果我理解正确的话,你想要动态的、运行时的属性观察。做这样的事情(在 self 上实现这个方法,你想要内省的类):

#import <objc/runtime.h>

- (NSArray *)allPropertyNames
{
    unsigned count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);

    NSMutableArray *rv = [NSMutableArray array];

    unsigned i;
    for (i = 0; i < count; i++)
    {
        objc_property_t property = properties[i];
        NSString *name = [NSString stringWithUTF8String:property_getName(property)];
        [rv addObject:name];
    }

    free(properties);

    return rv;
}

- (void *)pointerOfIvarForPropertyNamed:(NSString *)name
{
    objc_property_t property = class_getProperty([self class], [name UTF8String]);

    const char *attr = property_getAttributes(property);
    const char *ivarName = strchr(attr, 'V') + 1;

    Ivar ivar = object_getInstanceVariable(self, ivarName, NULL);

    return (char *)self + ivar_getOffset(ivar);
}

像这样使用它:

SomeType myProperty;
NSArray *properties = [self allPropertyNames];
NSString *firstPropertyName = [properties objectAtIndex:0];
void *propertyIvarAddress = [self getPointerOfIvarForPropertyNamed:firstPropertyName];
myProperty = *(SomeType *)propertyIvarAddress;

// Simpler alternative using KVC:
myProperty = [self valueForKey:firstPropertyName];

希望这可以帮助。

于 2012-08-02T09:05:21.777 回答
12

使用NSObject 的attributeKeys方法。

    for (NSString *key in [self attributeKeys]) {

        id attribute = [self valueForKey:key];

        if([attribute isKindOfClass:[UILabel  class]])
        {
         //put attribute to your array
        }
    }
于 2012-08-02T10:08:56.653 回答
8

看看这个链接。它是目标 C 运行时的目标 c 包装器。

您可以使用如下代码

uint count;
objc_property_t* properties = class_copyPropertyList(self.class, &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);
于 2012-08-02T09:03:52.503 回答
6

您必须包含运行时标头

 #import<objc/runtime.h>
uint propertiesCount;
objc_property_t *classPropertiesArray = class_copyPropertyList([self class], &propertiesCount);
free(classPropertiesArray);
于 2012-08-02T09:20:46.603 回答
1

@user529758 的答案不适用于 ARC,也不会列出任何祖先类的属性。

要解决此问题,您需要向上遍历类层次结构,并使用 ARC 兼容[NSObject valueForKey:]来获取属性值。

人.h:

#import <Foundation/Foundation.h>

extern NSMutableArray *propertyNamesOfClass(Class klass);

@interface Person : NSObject

@property (nonatomic) NSString *name;

@end

人.m:

#import "Person.h"
#import <objc/runtime.h>

NSMutableArray *propertyNamesOfClass(Class klass) {
    unsigned int count;
    objc_property_t *properties = class_copyPropertyList(klass, &count);

    NSMutableArray *rv = [NSMutableArray array];

    for (unsigned int i = 0; i < count; i++)
    {
        objc_property_t property = properties[i];
        NSString *name = [NSString stringWithUTF8String:property_getName(property)];
        [rv addObject:name];
    }

    free(properties);

    return rv;
}

@implementation Person

- (NSMutableArray *)allPropertyNames {
    NSMutableArray *classes = [NSMutableArray array];
    Class currentClass = [self class];
    while (currentClass != nil && currentClass != [NSObject class]) {
        [classes addObject:currentClass];
        currentClass = class_getSuperclass(currentClass);
    }

    NSMutableArray *names = [NSMutableArray array];
    [classes enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(Class currentClass, NSUInteger idx, BOOL *stop) {
        [names addObjectsFromArray:propertyNamesOfClass(currentClass)];
    }];

    return names;
}

- (NSString*)description {
    NSMutableArray *keys = [self allPropertyNames];
    NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithCapacity:keys.count];
    [keys enumerateObjectsUsingBlock:^(NSString *key, NSUInteger idx, BOOL *stop) {
        properties[key] = [self valueForKey:key];
    }];

    NSString *className = NSStringFromClass([self class]);
    return [NSString stringWithFormat:@"%@ : %@", className, properties];
}

学生.h:

#import "Person.h"

@interface Student : Person

@property (nonatomic) NSString *studentID;

@end

学生.m:

#import "Student.h"

@implementation Student

@end

主.m:

#import <Foundation/Foundation.h>
#import "Student.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        Student *student = [[Student alloc] init];
        student.name = @"John Doe";
        student.studentID = @"123456789";
        NSLog(@"student - %@", student);
    }
    return 0;
}
于 2016-05-20T18:54:33.397 回答
-1

serhats 的解决方案非常棒,不幸的是它不适用于 iOS(正如你所提到的)(并且这个问题被标记为 iOS)。一种解决方法是获取对象的 NSDictionary 表示,然后将其作为键值对正常访问。我会为 NSObject 推荐一个类别:

头文件:

@interface NSObject (NSDictionaryRepresentation)

/**
 Returns an NSDictionary containing the properties of an object that are not nil.
 */
- (NSDictionary *)dictionaryRepresentation;

@end

实施文件:

#import "NSObject+NSDictionaryRepresentation.h"
#import <objc/runtime.h>

@implementation NSObject (NSDictionaryRepresentation)

- (NSDictionary *)dictionaryRepresentation {
    unsigned int count = 0;
    // Get a list of all properties in the class.
    objc_property_t *properties = class_copyPropertyList([self class], &count);

    NSMutableDictionary *dictionary = [[NSMutableDictionary alloc] initWithCapacity:count];

    for (int i = 0; i < count; i++) {
        NSString *key = [NSString stringWithUTF8String:property_getName(properties[i])];
        NSString *value = [self valueForKey:key];

        // Only add to the NSDictionary if it's not nil.
        if (value)
            [dictionary setObject:value forKey:key];
    }

    free(properties);

    return dictionary;
}

@end

借用这篇文章:http ://hesh.am/2013/01/transform-properties-of-an-nsobject-into-an-nsdictionary/

这样你就可以做一些类似于 serhats 提到的事情:

for (NSString *key in objectDic.allKeys) {
   if([objectDic[key] isKindOfClass:[UILabel  class]])
   {
       //put attribute to your array
   }
}
于 2015-07-02T10:10:57.530 回答