美文网首页
MJExtension中值得思考的地方

MJExtension中值得思考的地方

作者: petyou | 来源:发表于2018-04-20 11:48 被阅读0次

1.MJProperty

  • setOriginKey:forClass
/** 对应着字典中的key */
- (void)setOriginKey:(id)originKey forClass:(Class)c
{
    // NSString类型的key是一般情况
    if ([originKey isKindOfClass:[NSString class]]) { // 字符串类型的key
        NSArray *propertyKeys = [self propertyKeysWithStringKey:originKey];
        if (propertyKeys.count) {
            [self setPorpertyKeys:@[propertyKeys] forClass:c];
        }
    }
     /* NSArray类型的key,是应对服务端多人开发且不规范操作导致使用不同字段表达同一个意思.
     比如为了表示一个ID,可能有这些不同字段返回给客户端:id Id iD ID.使用一组key去读取某个
     可能变化的字段.
     */
    else if ([originKey isKindOfClass:[NSArray class]]) {
        NSMutableArray *keyses = [NSMutableArray array];
        for (NSString *stringKey in originKey) {
            NSArray *propertyKeys = [self propertyKeysWithStringKey:stringKey];
            if (propertyKeys.count) {
                [keyses addObject:propertyKeys];
            }
        }
        if (keyses.count) {
            [self setPorpertyKeys:keyses forClass:c];
        }
    }
}
  • cachedPropertyWithProperty
/* 这个方法将每个类下面的属性都缓存在MJPrperty中,但是在NSObject+MJProperty这个类中,
   NSObject+MJProperty这个类的
   + (NSMutableArray *)properties{...}中又尝试将一个类的所有属性缓存在静态的全局字典中.重复缓存了.
 */
+ (instancetype)cachedPropertyWithProperty:(objc_property_t)property
{
    MJProperty *propertyObj = objc_getAssociatedObject(self, property);
    if (propertyObj == nil) {
        propertyObj = [[self alloc] init];
        propertyObj.property = property;
        objc_setAssociatedObject(self, property, propertyObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    return propertyObj;
}

/* NSObject+MJProperty中 */
+ (NSMutableArray *)properties
{
    NSMutableArray *cachedProperties = [self dictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)];
    
    if (cachedProperties == nil) {
        cachedProperties = [NSMutableArray array];
        
        [self mj_enumerateClasses:^(__unsafe_unretained Class c, BOOL *stop) {
            // 1.获得所有的成员变量
            unsigned int outCount = 0;
            objc_property_t *properties = class_copyPropertyList(c, &outCount);
            
            // 2.遍历每一个成员变量
            for (unsigned int i = 0; i<outCount; i++) {
                MJProperty *property = [MJProperty cachedPropertyWithProperty:properties[i]];
                // 过滤掉系统自动添加的元素
                if ([property.name isEqualToString:@"hash"]
                    || [property.name isEqualToString:@"superclass"]
                    || [property.name isEqualToString:@"description"]
                    || [property.name isEqualToString:@"debugDescription"]) {
                    continue;
                }
                property.srcClass = c;
                [property setOriginKey:[self propertyKey:property.name] forClass:self];
                [property setObjectClassInArray:[self propertyObjectClassInArray:property.name] forClass:self];
                [cachedProperties addObject:property];
            }
            
            // 3.释放内存
            free(properties);
        }];
        
        // 将一个类的所有属性缓存在静态的全局字典中
        [self dictForKey:&MJCachedPropertiesKey][NSStringFromClass(self)] = cachedProperties;
    }
    
    return cachedProperties;
}

2.NSObject+MJClass

  • mj_setupBlockReturnValue: key
+ (void)mj_setupBlockReturnValue:(id (^)(void))block key:(const char *)key
{
    if (block) {
        objc_setAssociatedObject(self, key, block(), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    } else {
        objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    /* 在静态设置allowedProperty或者ignoredPorperty后,清空字典中的数据,那么下一次取空的时候,
       才能通过动态方法和静态方法重新获取一遍,添加到掉全局静态字典中.
       弊端是为了更新A类的缓存,把B类的缓存也清除了.
        Q:在字典中,明明以每个类的class为key,为何不单独去除掉这个类的焕缓存数据呢?A:因为父类子类的相互影响.
        父类添加静态设置,子类也需要重新生成数据.如果每添加一个类,就遍历所有key,查询是否有当前类的父类或者子
        类,这样带来的消耗高于直接去掉所有的缓存.
     */
    [[self dictForKey:key] removeAllObjects];
}
  • 在NSObject+MJClass和NSObject+MJProperty这两个NSObject的分类中同时存在一个同名的方法这显然会导致只有一个分类中的缓存是有效,我提了issue,不知道何时会更新掉这个bug
/* NSObject+MJClass中的 */
+ (NSMutableDictionary *)dictForKey:(const void *)key
{
    if (key == &MJAllowedPropertyNamesKey) return allowedPropertyNamesDict_;
    if (key == &MJIgnoredPropertyNamesKey) return ignoredPropertyNamesDict_;
    if (key == &MJAllowedCodingPropertyNamesKey) return allowedCodingPropertyNamesDict_;
    if (key == &MJIgnoredCodingPropertyNamesKey) return ignoredCodingPropertyNamesDict_;
    return nil;
}

/* NSObject+MJProperty中的 */
+ (NSMutableDictionary *)dictForKey:(const void *)key
{
    if (key == &MJReplacedKeyFromPropertyNameKey) return replacedKeyFromPropertyNameDict_;
    if (key == &MJReplacedKeyFromPropertyName121Key) return replacedKeyFromPropertyName121Dict_;
    if (key == &MJNewValueFromOldValueKey) return newValueFromOldValueDict_;
    if (key == &MJObjectClassInArrayKey) return objectClassInArrayDict_;
    if (key == &MJCachedPropertiesKey) return cachedPropertiesDict_;
    return nil;
}

3.NSObject+MJKeyValue

  • mj_setKeyValues:context 这个方法中增加了类型匹配的判断,某个版本之前在自动转换(比如 NSString 和 NSNumber 之间的转换)不能完成时,它会直接把 JSON 对象赋值给类型不匹配的 Model 属性 这样的结果会导致稍后 Model 在使用时,造成潜在的 Crash 风险. 就比如我的项目中就遇到原本某个字段是对应一个数组,结果在异常情况下,直接给了一个<>
    // value和property类型不匹配
                if (propertyClass && ![value isKindOfClass:propertyClass]) {
                    value = nil;
                }

4.NSString+MJExtension

  • url使用ASCII码进行传输特殊字符和保留字符都要进行编码才能保证在解码时不会引起url语义的转变
- (NSURL *)mj_url
{
//    [self stringByAddingPercentEncodingWithAllowedCharacters:[NSCharacterSet characterSetWithCharactersInString:@"!$&'()*+,-./:;=?@_~%#[]"]];
    // !$&'()*+,-./:;=?@_~%#[]中特殊字符有 -_.~ 这四个,其它的是保留字符,这些都需要编码
    return [NSURL URLWithString:(NSString *)CFBridgingRelease(CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, (CFStringRef)self, (CFStringRef)@"!$&'()*+,-./:;=?@_~%#[]", NULL,kCFStringEncodingUTF8))];
}

相关文章

网友评论

      本文标题:MJExtension中值得思考的地方

      本文链接:https://www.haomeiwen.com/subject/jsuakftx.html