名單

2012-08-02 29 views
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]???)

+7

不要將其命名與'get方法'前綴;這僅限於一個非常具體的用例,這不是它。 – bbum 2012-08-02 14:05:52

+4

在iOS上,請考慮使用IBOutletCollection。這幾乎是它存在的。 http://www.bobmccune.com/2011/01/31/using-ios-4s-iboutletcollection/ – isaac 2013-09-05 02:02:03

回答

76

所以更準確地說,你想動態,運行時observaion性質的,如果我沒有得到它。做這樣的事情(實現自我這種方法,您希望類反思):

#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]; 

希望這有助於。

+0

這個作品真的很不錯!只有在這之後我如何訪問這些屬性?這給了我字符串的名稱,但沒有實際的指向對象......它一定很容易,我猜)我只是不熟悉OBJC /運行時類:)謝謝! – 2012-08-02 09:41:40

+0

要更conrete,像 - id oneProperty =屬性... – 2012-08-02 09:43:17

+0

@animal_chin我更新了我的答案。 – 2012-08-02 09:59:51

7

看看這個link。它是在目標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); 
+0

不要忘記#import Jonathan 2016-08-23 11:52:34

6

必須包括運行時頭

#import<objc/runtime.h> 
uint propertiesCount; 
objc_property_t *classPropertiesArray = class_copyPropertyList([self class], &propertiesCount); 
free(classPropertiesArray); 
+0

編輯答案,感謝您的更正 – Vlad 2012-08-02 09:37:36

11

使用NSObject中的attributeKeys方法。

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

     id attribute = [self valueForKey:key]; 

     if([attribute isKindOfClass:[UILabel class]]) 
     { 
     //put attribute to your array 
     } 
    } 
+12

這看起來像最優雅的解決方案,但可悲的方法attributeKeys只適用於Mac OS X,不在iOS中... – 2012-08-02 12:19:41

-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 
    } 
} 
0

答案通過@ user529758不會與ARC工作,它不會顯示任何祖先類的屬性。

要解決此問題,您需要遍歷類層次結構,並使用兼容ARC的[NSObject valueForKey:]來獲取屬性值。

Person.h:

#import <Foundation/Foundation.h> 

extern NSMutableArray *propertyNamesOfClass(Class klass); 

@interface Person : NSObject 

@property (nonatomic) NSString *name; 

@end 

人。L:

#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]; 
} 

Student.h:

#import "Person.h" 

@interface Student : Person 

@property (nonatomic) NSString *studentID; 

@end 

Student.m:

#import "Student.h" 

@implementation Student 

@end 

的main.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; 
}