如何使用objective-c运行时

时间:2015-06-02 05:49:24

标签: objective-c runtime

在我的项目中有一个有很多变量的类,现在我希望它符合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,但我不知道如何解决它。

2 个答案:

答案 0 :(得分:0)

首先:在OOP中复制是一个复杂的问题,通用的90%解决方案通常都不是一个好主意。但是,......

如果您有非对象引用属性(NSIntegerdoublestruct等),则必须使用-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