在 Objective-C 中不可能将对象转换为子类的实例。但是,使用下面的类,您可以提供对象和子类的实例,并将所有属性的值复制到子类实例。此实现适用于 Objective-C 对象类型和 C 原语。您不必指定(甚至确定)需要复制的属性,只要您知道重要的变量是可见的并且可以设置(即,没有公开为“只读”的属性)完全暴露,其值无法由类重新计算)。因此,此方法对于已知类相对稳健,并且不需要更新以支持您在对象类中进行的适合这些参数的未来更改。它与 iOS 8 兼容。
该类提供了四种类方法:
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
将对象的所有属性复制到子类对象。如果 subclassObject 不是 object 的子类,则返回 nil。
+ (NSDictionary *) propertiesOfObject:(id)object;
返回对象所有可见属性的字典,包括来自其所有超类(NSObject 除外)的属性。
+ (NSDictionary *) propertiesOfClass:(Class)class;
返回一个类的所有可见属性的字典,包括来自其所有超类(NSObject 除外)的那些。
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
返回一个包含特定子类的所有可见属性的字典。 不包括其超类的属性。
标题:
// SYNUtilities.h
#import <Foundation/Foundation.h>
@interface SYNUtilities : NSObject
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject;
+ (NSDictionary *) propertiesOfObject:(id)object;
+ (NSDictionary *) propertiesOfClass:(Class)class;
+ (NSDictionary *) propertiesOfSubclass:(Class)class;
@end
实施:
#import "SYNUtilities.h"
#import <objc/runtime.h>
#import <objc/message.h>
@implementation SYNUtilities
+ (id) copyObject:(id)object toSubclassObject:(id)subclassObject
{
if (![[subclassObject class] isSubclassOfClass:[object class]]) {
return nil;
}
NSDictionary * properties = [self propertiesOfObject:object];
NSLog(@"Properties of %@:\n%@", [object class], properties);
for (NSString * property in properties) {
SEL selector = NSSelectorFromString(property);
if (selector) {
id value = [object valueForKey:property];
[subclassObject setValue:value forKey:property];
}
}
return subclassObject;
}
+ (NSDictionary *) propertiesOfObject:(id)object
{
Class class = [object class];
return [self propertiesOfClass:class];
}
+ (NSDictionary *) propertiesOfClass:(Class)class
{
if (class == NULL) {
return nil;
}
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