我想使用
- (id)initWithCoder:(NSCoder *)coder
和
- (void)encodeWithCoder:(NSCoder *)coder
对自定义类进行编码以进行复制(使用NSKeyedArchiver等)
我的自定义类包含大量 iVar。 与其在编码代码中列出所有这些,我想使用反射来简单地枚举自定义类中的所有属性,并从循环中对每个属性进行编码/解码。
我最终会得到看起来像这样的代码:
- (id)initWithCoder:(NSCoder *)coder
{
if ((self = [super init]))
{
[self codeWithCoder:coder andDirection:0];
}
}
- (void)encodeWithCoder:(NSCoder *)coder
{
[self codeWithCoder:coder andDirection:1];
}
-(void)codeWithCoder:(NSCoder *)coder andDirection:(NSInteger)direction
{
unsigned count;
objc_property_t *properties = class_copyPropertyList([self class], &count);
unsigned i;
for (i = 0; i < count; i++)
{
objc_property_t property = properties[i];
NSString *name = [NSString stringWithUTF8String:property_getName(property)];
const char * type = property_getAttributes(property);
NSString * typeString = [NSString stringWithUTF8String:type];
NSArray * attributes = [typeString componentsSeparatedByString:@","];
NSString * typeAttribute = [attributes objectAtIndex:0];
NSString * propertyType = [typeAttribute substringFromIndex:1];
const char * rawPropertyType = [propertyType UTF8String];
if (strcmp(rawPropertyType, @encode(float)) == 0) {
//it's a float
if (direction==0) ??? = [coder decodeFloatForKey:name];
else [coder encodeFloat:???? forKey:name];
} else if (strcmp(rawPropertyType, @encode(bool)) == 0) {
//it's a bool
if (direction==0) ??? = [coder decodeBoolForKey:name];
else [coder encodeBool:???? forKey:name];
} else if (strcmp(rawPropertyType, @encode(int)) == 0) {
//it's an int
if (direction==0) ??? = [coder decodeIntegerForKey:name];
else [coder encodeInteger:???? forKey:name];
} else if (strcmp(rawPropertyType, @encode(id)) == 0) {
//it's some sort of object
if (direction==0) ??? = [coder decodeObjectForKey:name];
else [coder encodeObject:???? forKey:name];
}
}
free(properties);
}
我的问题是:我用什么替换???和????片段来获取和设置实际的 ivar 值?
你试过吗:
object_setInstanceVariable
object_setIvar
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/ObjCRuntimeRef/
您可能希望调用class_copyIvarList而不是属性版本 - 仅获取属性可能会错过 IVars,具体取决于您的用例。 你的超级类也采用NSCoding吗? 你必须调用 initWithCoder 和 encodeWithCoder 的超级实现。 另请注意,class_copyIvarList 和 class_copyPropertyList 只返回当前类中定义的内容,而不返回超类中定义的内容。
基本上,您使用 objective-C 的运行时 API ( class_copyIvarList
) 获取对象的实例变量,然后检查每个实例变量的类型 ( ivar_getTypeEncoding
),并根据类型动态编码/解码。
性能应该是一个问题,因为每次调用class_copyIvarList
都很耗时。库DYCoding
很好地解决了这个问题,因为它使用 imp_implementationWithBlock
和 class_addMethod
将实现添加到类中,因此每个类只调用一次class_copyIvarList
。
参考:可以查 https://github.com/flexme/DYCoding,它提供动态编码/解码,速度和预编译代码一样快。
我们可以使用 KVO 来解决这个问题。
// decode
id value = [decoder decodeObjectForKey:name];
if (value != nil) {
[self setValue:value forKey:name];
}
// encode
id value = [self valueForKey:name];
if ([value respondsToSelector:@selector(encodeWithCoder:)]) {
[encoder encodeObject:value forKey:name];
}
参考:运行时NSCoding,它显示了一个完整的解决方案。