在运行时循环遍历所有对象属性

9

我想创建一个基于Objective-C的类,在运行时对所有属性(不同类型的)执行操作。由于属性的名称和类型并不总是已知的,因此我该如何实现这样的操作?

@implementation SomeBaseClass

- (NSString *)checkAllProperties
{
    for (property in properties) {
        // Perform a check on the property
    }
}

编辑:在自定义- (NSString *)description:重写中,这将非常有用。

3个回答

22

对于 mvds 的回答进行补充(在看到他的回答之前我就开始写了),这里有一个使用 Objective-C 运行时 API 的小样例程序,可以循环遍历并打印类中每个属性的信息:

#import <Foundation/Foundation.h>
#import <objc/runtime.h>

@interface TestClass : NSObject

@property (nonatomic, retain) NSString *firstName;
@property (nonatomic, retain) NSString *lastName;
@property (nonatomic) NSInteger *age;

@end

@implementation TestClass

@synthesize firstName;
@synthesize lastName;
@synthesize age;

@end

int main(int argc, char *argv[]) {
    @autoreleasepool {
        unsigned int numberOfProperties = 0;
        objc_property_t *propertyArray = class_copyPropertyList([TestClass class], &numberOfProperties);

        for (NSUInteger i = 0; i < numberOfProperties; i++)
        {
            objc_property_t property = propertyArray[i];
            NSString *name = [[NSString alloc] initWithUTF8String:property_getName(property)];
            NSString *attributesString = [[NSString alloc] initWithUTF8String:property_getAttributes(property)];
            NSLog(@"Property %@ attributes: %@", name, attributesString);
        }
        free(propertyArray);
    }
}

输出:

属性age的属性: T^q,Vage
属性lastName的属性: T@"NSString",&,N,VlastName
属性firstName的属性: T@"NSString",&,N,VfirstName

请注意,此程序需要开启ARC编译。


一个好的后续问题是如何动态返回每个值... 我正在考虑动态创建一个访问器,但从语法上来说,我仍在努力解决这个问题。 - Old McStopher
1
有几种方法可以做到这一点,但最直接的方法是使用KVC:id value = [self valueForKey:@"propertyName"]。当你同时拥有基本类型(int、float等)和对象类型(NSString等)的返回类型时,会变得稍微复杂一些,但基本原理仍然适用。 - Andrew Madsen
它应该使用开启ARC编译(我在回答中应该提到这一点)。 - Andrew Madsen
[self valueForKey:@"propertyName"] 对于基本类型有效,那么对于自定义类型该怎么获取属性呢?谢谢! - flypig
1
我可能不完全理解你的问题。对于类型为自定义类的属性,[self valueForKey:@"propertyName"] 可以正常工作。-valueForKey: 的返回类型是 id。 - Andrew Madsen

15
使用
objc_property_t * class_copyPropertyList(Class cls, unsigned int *outCount)

并且阅读 https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ObjCRuntimeRef/Reference/reference.html 以了解如何确切地执行此操作。

以下是一些可帮助你入门的代码:

#import <objc/runtime.h>

unsigned int count=0;
objc_property_t *props = class_copyPropertyList([self class],&count);
for ( int i=0;i<count;i++ )
{
    const char *name = property_getName(props[i]); 
    NSLog(@"property %d: %s",i,name);
}

1
卓越的资源,非常适合于更接近底层的运行时疯狂。 - Old McStopher
非常感谢,运行良好。我如何在运行时将一个对象的属性复制到另一个对象? - Pavan
1
@PavanSaberjack 使用 valueForKey:setValue:forKey:,需要注意 char *name 不能直接作为对象(例如 NSString)使用,需要进行某种形式的转换。 - mvds
@mvds 我遇到了一个问题,即在子类中使用class_copyPropertyList代码时,它不会返回父类的属性列表。你有什么解决方法吗? - Laky

0

对@mvds的回答进行补充:

unsigned int count=0;
objc_property_t *props = class_copyPropertyList([self class],&count);
for ( int i=0;i<count;i++ )
{
    const char *name = property_getName(props[i]);
    NSString* dataToGet = [NSString swf:@"%s",name];
    @try { // in case of this pair not key value coding-compliant
        id value = [barButton valueForKey:dataToGet];
        NSLog(@"prop %d: %s  %@",i,name, value);
    } @catch (NSException *exception) {
        // NSLog(@"Exception:%@",exception);
    }
    @finally {
        // Display Alternative
    }
}

请给 @mvds 点个赞。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接