在我的项目中有一个有很多变量的类,现在我希望它符合NSCopying
协议,所以我必须“复制”- (id)copyWithZone:(NSZone *)zone
中的每个变量。如果变量是一个对象,则向其发送副本,如果它是标量值,则只需使用赋值符号。
该类可能经常更改,这意味着我之后可能会添加一些变量,然后我或其他人可能忘记修改- (id)copyWithZone:(NSZone *)zone
方法中的代码。为了避免它,我正在考虑使用objective-c的运行时功能来完成工作。
下面是Class的一部分,类中的变量是ether一个对象或一个标量值。您会注意到一个unsigned int数组,这是一个例外,您可以省略它,因为我可以在变量的类型编码为[10I]
时手动复制它。
@interface DataObject : NSObject <NSCopying>
{
@public
unsigned int arrPrice[10];
}
@property (nonatomic, copy) NSString *code ;
@property (nonatomic, assign) unsigned char type ;
@property (nonatomic, assign) int digit ;
@property (nonatomic, assign) unsigned int count ;
@property (nonatomic, assign) unsigned short time ;
@property (nonatomic, assign) char season ;
@property (nonatomic, assign) int64_t amount ;
@property (nonatomic, strong) NSMutableArray *mArray ;
@property (nonatomic, assign) BOOL isOk ;
@end
copyWithZone:
- (id)copyWithZone:(NSZone *)zone
{
DataObject *obj = [[DataObject allocWithZone:zone] init] ;
unsigned int uVarCount = 0 ;
Ivar *pVarList = class_copyIvarList(self.class, &uVarCount) ;
for (unsigned int i = 0; i < uVarCount; ++i) {
Ivar *pVar = pVarList+i ;
const char *name = ivar_getName(*pVar) ;
const char *typeEncoding = ivar_getTypeEncoding(*pVar) ;
NSString *strTypeEncoding = [NSString stringWithUTF8String:typeEncoding] ;
if ([strTypeEncoding isEqualToString:@"[I10]"]) {
// its arrPrice, use memcpy to copy
memcpy(obj->arrPrice, self->arrPrice, sizeof(arrPrice)) ;
continue ;
} else if ([strTypeEncoding hasPrefix:@"@"]) {
// its a object
id o = object_getIvar(self, *pVar) ;
o = [o copy] ;
object_setIvar(obj, *pVar, o) ;
NSLog(@"var name:%s, type:%s, value:%@", name, typeEncoding, o) ;
} else {
// runtime error
id o = object_getIvar(self, *pVar) ;
object_setIvar(obj, *pVar, o) ;
}
}
free(pVarList) ;
return obj ;
}
当变量不是对象时,我发现运行时错误,我发现why,但我不知道如何解决它。
答案 0 :(得分:0)
首先:在OOP中复制是一个复杂的问题,通用的90%解决方案通常都不是一个好主意。但是,......
如果您有非对象引用属性(NSInteger
,double
,struct
等),则必须使用-object_getInstanceVariable ()
。但事情可能会更复杂。
让我们先尝试一下:
} else {
long *valuePointer;
object_getInstanceVariable(self, ivar_getName(pVar), & valuePointer) ;
object_setInstanceVariable(obj, ivar_getName(pVar), valuePointer) ;
}
如您所见,我使用指向long
的指针。如果属性类型为long
,这是合法的。 (并且对于与long
大小相同的其他类型可能是合法的。)关于其他类型,尤其是struct
类型,您不能这样做,因为它们可以具有不同的大小。因此,要获得完整的解决方案,您必须分析编码字符串。
至少在这一点上你应该再想一想,通用副本是否是个好主意。我更喜欢不分支。
答案 1 :(得分:0)
确实很难找到理想的解决方案,但我的目标是支持大多数标量值和Objective-c对象。
主要思想是使用ivar_getOffset
来获取ivar的位置并直接修改它。为了达到这个目的,我还需要知道ivar的大小,所以我创建了一个方法- (unsigned int)sizeOfTypeEncoding:(NSString *)typeEncoding supportOrNot:(BOOL *)support
来计算ivar的大小并返回它,如果ivar类型不支持,*support
将{ {1}}并抛出异常。
因此,当我向课程添加不受支持的ivar时,异常可以告诉我像NO
一样手动执行。
arrPrice