Mantle的一个坑被我踩到了,快来围观

   最近用到了一个Mantle这个库的一个功能点,就是通过获取MTLModel的hash值,来唯一区分不同的model对象。(这些model可能属于同一个类,也可能不属于同一个类),印象中MTLModel对hash方法进行了重写,就是为了实现这个目的,我兴冲冲的开始要使用了,但是为了保险起见,我这边进行了相关的单元测试,发现这个方法还是有一些问题的。

MTLModel中的hash方法实现如下:

- (NSUInteger)hash {
    NSUInteger value = 0;

    for (NSString *key in self.class.propertyKeys) {
        value ^= [[self valueForKey:key] hash];
    }

    return value;
}

可以看出主要是对属性值取hash值操作,然后进行相应的异或操作。
为了测试方便,我这边使用了kiwi的测试框架
新建了一个类JKModel

#import 

@interface JKModel : MTLModel

@property (nonatomic,copy) NSString *name;
@property (nonatomic,copy) NSString *home;

@end

测试代码如下:
Mantle的一个坑被我踩到了,快来围观_第1张图片

大家可以看到单元测试没有通过,说明这两种情况下并不能通过hash值来作为model的唯一标识。通过对MTLModel的源码分析,我们发现重写的hash方法并没有对key进行相关的处理,没有体现出key,value的一一对应关系,只要是两个model的有相关的值,哪怕是key没有对应,依然会判断为model的hash值相等。为了解决这个问题,我新写了一个类对hash方法进行适当的改造。

- (NSUInteger)hash {
    NSUInteger value = 0;

    for (NSString *key in [[self class] getProperties]) {
        value ^= [[NSString stringWithFormat:@"%@&&%lu",key,[[self valueForKey:key] hash]] hash];

    }

    return value;
}

+ (NSArray *)getProperties {
    //用于存入属性数量
    unsigned int outCount = 0;
    //获取属性数组
    objc_property_t *propertyList = class_copyPropertyList([self class], &outCount);

    NSMutableArray *arrM = [NSMutableArray arrayWithCapacity:outCount];
    //遍历数组
    for (int i = 0; i < outCount; ++i) {
        objc_property_t property = propertyList[i];
        //获取属性名
        const  char *cName = property_getName(property);
        //将其转换成c字符串
        NSString *propertyName = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
        //        加入数组
        [arrM addObject:propertyName];
    }
    //在使用了c函数的creat, copy等函数是记得手动释放,要不然会引起内存泄露问题
    free(propertyList);
    return arrM.copy;

}

然后我们重新进行测试,看看效果如何。

SPEC_BEGIN(TDModelSpec)

describe(@"TDModel", ^{
    context(@"测试BaseModel hash方法", ^{
        it(@"如果两个model对象情形1", ^{
            TDModel *model1 = [TDModel new];
            model1.name =@"张三";
            model1.home = nil;

            TDModel *model2 = [TDModel new];
            model2.name =nil;
            model2.home = @"张三";
            [[theValue([model1 hash]) shouldNot]  equal:theValue([model2 hash])];
        });

        it(@"如果两个model对象情形2", ^{
            TDModel *model1 = [TDModel new];
            model1.name =@"张三";
            model1.home = @"111";

            TDModel *model2 = [TDModel new];
            model2.name =@"111";
            model2.home = @"张三";
            [[theValue([model1 hash]) shouldNot]  equal:theValue([model2 hash])];
        });
    });
});

SPEC_END

测试结果通过。
大家如果在使用到这个点的时候需要注意一下哦。
demo如下:http://download.csdn.net/download/hhl110120/9941950
注:没打算要积分的,现在必须要设置积分了,大家如果没有积分的话,可以私信我

如果有哪里考虑的不正确,或者不完善的欢迎大家批评指正,一块交流进步。

你可能感兴趣的:(Object-C,IOS)