预告 ARC 重写:iOS 实现的 json 数据源的 O-R Mapping-阿里云开发者社区

开发者社区> 悟静> 正文

预告 ARC 重写:iOS 实现的 json 数据源的 O-R Mapping

简介: iOS 实现的 json 数据源的 O-R Mapping 太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循“署名-非商业用途-保持一致”创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作。
+关注继续查看

iOS 实现的 json 数据源的 O-R Mapping

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es)

本文遵循“署名-非商业用途-保持一致”创作公用协议

转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS、Android、Html5、Arduino、pcDuino否则,出自本博客的文章拒绝转载或再转载,谢谢合作。



时隔几年之久,再来看这四天写出的成果,不禁对那时的自已心生敬佩。

想来,现在看这堆风格很合理的代码,也有些难度,

因为,其中有几个 OC 的类继承的点,是不能用常规面向对象继承来理解的,

而是借鉴 Linux 底层中 C 语言实现的对象机制,逐层提取完成。

确实这一部分当时费了很多脑汁!


当时用 MRC 写成,现在 ARC 已经基本取代了 MRC,而 Swift 在今年的稍晚些时侯,也会取代 ARC 成为常规编程语言而应用于实际项目开发中,

所以,在 ARC 尚在的时侯,把这一课补上吧,别留下遗憾,真正开始了 Swift 的时侯,就不会再有心力和驱动力去使用 ARC 了。!#)*$&#^^*


本处仅为预告,至少是否真得能够重写,还看机缘吧。 

------------------------


再挖个坑,这个相对复杂一些,当时花了四天时间,在2010年的时侯,对于Objective-C 的反射机制的研究。

其中的设计思想,大多源自 Android 教父高焕堂先生的基类架构思想,我这里只是取了其中一小点点,拿到iOS上用一用。

曾经参加过高焕堂先生在我们公司讲授的近一个月的Android课程,Android上来说学到什么,都是次要的,最重要的是在架构设计思想上得到了真经。

高焕堂:台湾Android论坛主席,现任亚太地区Android技术大会主席,台湾Android领域框架开发联盟总架构师。发表100多篇Android核心技术文章,出版了6本Android专业技术书籍。专精于Android 核心框架及核心服务程序开发。


以下仅为相关核心类部分,涉及业务逻辑部分均已删除,不便透露。

其它实体类从该基类派生,各子类成员需有如下限定:

 1实体必须继承自BaseEntity,且该实体类成员变量只能是如下类型的对象:

 NSString

 NSNumber

 NSDecimalNumber

 UIImage

 NSArray

 BaseEntity的子类

  2不允许使用NSDictionary,如确需要,可考虑定义对应实体类,且该实体类必须继承自BaseEntity

  3禁止有继承关系的类进行复合、聚集。

        任何循环引用的情况均需考虑是否会造成解析死循环,比如,a对象有一个数组成员,那么该数组的元素不能是a对象及其子类;

  4

       a、数组元素值为NSNull,用空字符串代替

       b、成员变量值为nil,解析会自动忽略该成员

       c、未在 中列出的类及其子类,解析会自动忽略

 

 // 以下类型转成json均协同测试通过

 数值:NSNumber

 整型

 长整型:最多传入10位数字,超过则溢出显示成负数

 单精度

 双精度

 布尔

 日期:NSDate

 字符串:NSString

 图片:UIImage

 数组:NSArray

 对象:BaseEntity的子类


@interface BaseEntity(Private)
//图片base64编解码方法
- (NSString *)encodeToBase64:(NSData *)toEncodeData;
- (NSData *)decodeFromBase64:(NSString *)toDecodeString;
- (NSString *)encodeUIImageToBase64:(UIImage *)toEncodeUIImage;
- (UIImage *)decodeUIImageFromBase64:(NSString *)toDecodeString;
//实体映射到键值
- (NSString *)parseAttributeType:(NSString *)Attributes;
- (NSArray *)getClassPropertys:(Class)classInstance;
- (NSArray *)getAllPropertys:(Class)classInstance;
- (NSDictionary *)getAllClassMapping:(Class)classInstance;
- (NSArray *)getPropertyList;
- (NSDictionary *)getClassMapping;
//键值映射到json
- (NSString *)generateRandomNumber;
- (NSArray *)getArrayContainSubedClass:(NSArray *)dealedArray;
- (NSDictionary *)getDictionaryContainedSubClass;
@end


@implementation BaseEntity


#pragma mark - 类生命周期

- (id)init
{
    self = [super init];
    if (self) {
        
    }
    
    return self;
}

- (void)dealloc {
    
    [super dealloc];
}


#pragma mark - 类属性映射表:各子如需替换json中的键值名,实现该方法

+ (NSDictionary *)getMappingDic {
    
    return [NSDictionary dictionaryWithObjectsAndKeys:@"tokenid", @"token", nil];
}

+ (NSDictionary *)dictionaryPresentationOf:(NSString *)className {
    
    Class subClass = NSClassFromString(className);
    BaseEntity *baseEntity = [[subClass alloc] init];
    NSDictionary *propertyDic = [NSDictionary dictionaryWithDictionary:[baseEntity dictionaryPresentation]];
    [baseEntity release];
    
    return propertyDic;
}

+ (NSString *)JSONRepresentationOf:(NSString *)className {
    
    Class subClass = NSClassFromString(className);
    BaseEntity *baseEntity = [[subClass alloc] init];
    NSString *jsonString = [NSString stringWithString:[baseEntity JSONRepresentation]];
    [baseEntity release];
    
    return jsonString;
}


#pragma mark - 图片base64编解码方法

- (NSString *)encodeToBase64:(NSData *)toEncodeData {
    
    NSUInteger shouldLength = ([toEncodeData length] + 2) / 3 * 3;
    NSUInteger base64Length = shouldLength / 3 * 4 + 1;
    
    char *base64Bytes = malloc(base64Length);
    memset(base64Bytes, 0, base64Length);
    
    encodeBase64([toEncodeData bytes], [toEncodeData length], base64Bytes, &base64Length);
    
    NSString *base64String = [[[NSString alloc] initWithUTF8String:base64Bytes] autorelease];
    
    return base64String;
}

- (NSData *)decodeFromBase64:(NSString *)toDecodeString {
    
    return nil;
}

- (NSString *)encodeUIImageToBase64:(UIImage *)toEncodeUIImage {
    NSData *imageData = UIImagePNGRepresentation(toEncodeUIImage);
    return [self encodeToBase64:imageData];
}

- (UIImage *)decodeUIImageFromBase64:(NSString *)toDecodeString {
    return nil;
}


#pragma mark - 实体映射到键值

- (NSString *)parseAttributeType:(NSString *)Attributes {
    NSRange leftRange = [Attributes rangeOfString:@"\""];
    NSString *temp1 = [Attributes substringFromIndex:leftRange.location+1];
    NSRange rightRange = [temp1 rangeOfString:@"\""];
    NSString *temp2 = [temp1 substringToIndex:rightRange.location];
    
    return temp2;
}

// 获取某个类自身的所有属性
- (NSArray *)getClassPropertys:(Class)classInstance {
    NSMutableArray *classPropertyDics = [[NSMutableArray alloc] init];
    
    u_int count;
    objc_property_t *properties  = class_copyPropertyList(classInstance, &count);
    for (int i = 0; i < count ; i++) {
        const char* propertyName = property_getName(properties[i]);
        const char* propertyAttributes = property_getAttributes(properties[i]);
        NSString *name = [NSString stringWithUTF8String:propertyName];
        NSString *attribute = [NSString stringWithUTF8String:propertyAttributes];
        NSString *attributeType = [self parseAttributeType:attribute];
        
        NSMutableDictionary *propertyDic = [[NSMutableDictionary alloc] init];
        [propertyDic setObject:attributeType forKey:name];
        [classPropertyDics insertObject:propertyDic atIndex:0];
        //[propertys addObject: propertyDic];
        [propertyDic release];
    }
    free(properties);
    
    return (NSArray *)classPropertyDics;
}

// 递归获取该类至其某一层父层(参数指定)的所有类的属性集合
- (NSArray *)getAllPropertys:(Class)classInstance {
    NSMutableArray *allPropertys = [[[NSMutableArray alloc] init] autorelease];
    
    if (![[[[[classInstance superclass] alloc] init] autorelease] isMemberOfClass:[BaseEntity class]]) {
        NSArray *superPropertys = [self getAllPropertys:[classInstance superclass]];
        [allPropertys addObjectsFromArray:superPropertys];
    }
    
    NSArray *propertys = [self getClassPropertys:classInstance];
    [allPropertys addObjectsFromArray:propertys];
    
    return allPropertys;
}

- (NSDictionary *)getAllClassMapping:(Class)classInstance {
    NSMutableDictionary *allMappingDic = [[[NSMutableDictionary alloc] init] autorelease];
    
    if (![[[[[classInstance superclass] alloc] init] autorelease] isMemberOfClass:[BaseEntity class]]) {
        NSDictionary *mappingDic = [self getAllClassMapping:[classInstance superclass]];
        NSArray *keys = [mappingDic allKeys];
        for (NSString *key in keys) {
            NSString *value = [mappingDic objectForKey:key];
            [allMappingDic setObject:value forKey:key];
        }
    }
    
    
    NSDictionary *classMappingDic = (NSDictionary *)[classInstance getMappingDic];
    NSArray *keys = [classMappingDic allKeys];
    for (NSString *key in keys) {
        NSString *value = [classMappingDic objectForKey:key];
        [allMappingDic setObject:value forKey:key];
    }
    
    return allMappingDic;
}

- (NSArray *)getPropertyList {
    NSArray *propertys = [self getAllPropertys:[self class]];
    
    return propertys;
}

- (NSDictionary *)getClassMapping {
    NSDictionary *mappingDic = [self getAllClassMapping:[self class]];
    
    return mappingDic;
}


#pragma mark - 键值映射到json

- (NSArray *)getArrayContainSubedClass:(NSArray *)dealedArray {
    NSMutableArray *outArray = [[[NSMutableArray alloc] init] autorelease];
    
    for (id element in dealedArray) {
        if (NO == [element isKindOfClass:[NSNull class]]) {
            //字符串
            if (YES == [element isKindOfClass:[NSString class]]) {
                [outArray addObject:element];
            }
            //数值
            else if (YES == [element isKindOfClass:[NSNumber class]]) {
                [outArray addObject:element];
            }
            //浮点型:暂未用,仅json转成对象时,浮点型会转成此类型
            else if (YES == [element isKindOfClass:[NSDecimalNumber class]]) {
                [outArray addObject:element];
            }
            //日期
            else if (YES == [element isKindOfClass:[NSDate class]]) {
                NSDate *attributeDateValue = (NSDate *)element;
                double attributeDoubleValue = [attributeDateValue timeIntervalSince1970];
                NSMutableDictionary *specialDic = [[NSMutableDictionary alloc] init];
                [specialDic setObject:[NSNumber numberWithLongLong:attributeDoubleValue] forKey:@"time"];
                [specialDic setObject:@"java.util.Date" forKey:@"javaClass"];
                [outArray addObject:specialDic];
                [specialDic release];
            }
            //图片
            else if (YES == [element isKindOfClass:[UIImage class]]) {
                NSString *base64String = [self encodeUIImageToBase64:(UIImage *)element];
                [outArray addObject:base64String];
            }
            //数组
            else if (YES == [element isKindOfClass:[NSArray class]]) {
                NSArray *dealedArray = [self getArrayContainSubedClass:(NSArray *)element];
                [outArray addObject:dealedArray];
            }
            //BaseEntity的子类
            else if (YES == [element isKindOfClass:[BaseEntity class]]) {
                NSDictionary *objDic = [element getDictionaryContainedSubClass];
                [outArray addObject:objDic];
            }
            else {
                //非以上类型的成员,此忽略,并提示开发人员,成员填写需进一步核实
                NSLog(@"忽略并且未转换成json的数组元素:%@", element);
            }
        }
        else {
            [outArray addObject:@""];
        }
    }
    
    return outArray;
}

- (NSDictionary *)getDictionaryContainedSubClass {
    NSArray *propertys = [self getPropertyList];
    NSDictionary *mappingDic = [self getClassMapping];
    
    NSMutableDictionary *selfDic = [[[NSMutableDictionary alloc] init] autorelease];
    for (NSDictionary *elementDic in propertys) {
        NSArray *keys = [elementDic allKeys];
        //正常来说,该层循环只有一次,特别情况例外(特别情况暂未知,留此接口)
        for (NSString *attributeName in keys) {
            //映射
            NSString *mappingKeyName = [mappingDic objectForKey:attributeName];
            if (nil == mappingKeyName) {
                mappingKeyName = attributeName;
            }
            
            id attributeValue = [self valueForKey:attributeName];
            if (nil != attributeValue) {
                //字符串
                if (YES == [attributeValue isKindOfClass:[NSString class]]) {
                    [selfDic setObject:attributeValue forKey:mappingKeyName];
                }
                //数值
                else if (YES == [attributeValue isKindOfClass:[NSNumber class]]) {
                    [selfDic setObject:attributeValue forKey:mappingKeyName];
                }
                //浮点型:暂未用,仅json转成对象时,浮点型会转成此类型
                else if (YES == [attributeValue isKindOfClass:[NSDecimalNumber class]]) {
                    [selfDic setObject:attributeValue forKey:mappingKeyName];
                }
                //日期
                else if (YES == [attributeValue isKindOfClass:[NSDate class]]) {
                    NSDate *attributeDateValue = (NSDate *)attributeValue;
                    double attributeDoubleValue = [attributeDateValue timeIntervalSince1970];
                    NSMutableDictionary *specialDic = [[NSMutableDictionary alloc] init];
                    [specialDic setObject:[NSNumber numberWithLong:attributeDoubleValue] forKey:@"time"];
                    [specialDic setObject:@"java.util.Date" forKey:@"javaClass"];
                    [selfDic setObject:specialDic forKey:mappingKeyName];
                    [specialDic release];
                }
                //图片
                else if (YES == [attributeValue isKindOfClass:[UIImage class]]) {
                    NSString *base64String = [self encodeUIImageToBase64:(UIImage *)attributeValue];
                    [selfDic setObject:base64String forKey:mappingKeyName];
                }
                //数组
                else if (YES == [attributeValue isKindOfClass:[NSArray class]]) {
                    NSArray *outArray = [self getArrayContainSubedClass:(NSArray *)attributeValue];
                    [selfDic setObject:outArray forKey:mappingKeyName];
                }
                //BaseEntity的子类
                else if (YES == [attributeValue isKindOfClass:[BaseEntity class]]) {
                    NSDictionary *objDic = [attributeValue getDictionaryContainedSubClass];
                    [selfDic setObject:objDic forKey:mappingKeyName];
                }
                else {
                    //非以上类型的成员,此忽略,并提示开发人员,成员填写需进一步核实
                    NSLog(@"忽略并且未转换成json的成员:%@", attributeName);
                }
            }
            else {
                [selfDic setObject:@"" forKey:mappingKeyName];
            }
        }
    }
    return selfDic;
}

- (NSDictionary *)dictionaryPresentation {
    return [self getDictionaryContainedSubClass];
}

- (NSString *)JSONRepresentation {
    NSDictionary *dictionaryPresentationDic = [self dictionaryPresentation];
    if ([dictionaryPresentationDic respondsToSelector:@selector(JSONRepresentation)]) {
        return [dictionaryPresentationDic JSONRepresentation];
    }
    else {
        return nil;
    }
}

- (NSString *)generateUuidString {
    CFUUIDRef uuid = CFUUIDCreate(kCFAllocatorDefault);
    NSString *uuidString = (NSString*)CFUUIDCreateString(kCFAllocatorDefault, uuid);
    [uuidString autorelease];
    CFRelease(uuid);
    return uuidString;
}

//按业务实际要求提供随机数算法,本处仅简单使用c语言随机数生成
- (NSString *)generateRandomNumber {
    int value = (arc4random() % 1000000000) + 1;
    
    return [NSString stringWithFormat:@"%d", value];
}

- (NSDictionary *)dictionaryPresentation:(NSString *)method {
    if (nil != method) {
        NSDictionary *businessDic = [self getDictionaryContainedSubClass];
        NSString *randomString = [self generateRandomNumber];
        
        NSMutableDictionary *resultDic = [[[NSMutableDictionary alloc] init] autorelease];
        [resultDic setObject:randomString forKey:@"id"];
        [resultDic setObject:method forKey:@"method"];
        NSArray *params = [NSArray arrayWithObject:businessDic];
        [resultDic setObject:params forKey:@"params"];
        
        return resultDic;
    }
    else {
        return nil;
    }
}

- (NSString *)JSONRepresentation:(NSString *)method {
    NSDictionary *dictionaryPresentationDic = [self dictionaryPresentation:method];
    if ([dictionaryPresentationDic respondsToSelector:@selector(JSONRepresentation)]) {
        return [dictionaryPresentationDic JSONRepresentation];
    }
    else {
        return nil;
    }
}

- (NSData *)JSONDataRepresentation:(NSString *)method {
    NSString *resultJsonString = [self JSONRepresentation:method];
    
    return (NSData *)[NSData dataWithBytes:[resultJsonString UTF8String] length:[resultJsonString lengthOfBytesUsingEncoding:NSUTF8StringEncoding]];
}

@end


此类是在我马上要出差去北京之前的四天时间里怱忙写成,费了不少脑汁,而且还费了不少口水说服小鬼儿们使用,不过还是得益于严大神的全力支持,感谢感谢再感谢,感谢在这里工作期间,大神对我的关照和支持,别人都觉得您出差不在家了,不用提心吊胆了,真爽!我听到您不在家去差了,心情正好想反,觉得您不在家了,就好像缺点儿什么似的,只要看着您在,我默默地工作,都觉得有底,再难的问题,都觉得能解决,因为即使我解决不了,还有您给指点。

现在每每行事,遇到难题,都会回想起,您当时处事的风格,宽容、大度、淡然、从容、果断、重情义......,随便挑出几样照着做,总会有豁然开朗的心情陪伴,虽然我不愿意也不可能再回去,但总还是忘不了您这位伯乐。

哈哈哈,看官们,上面这段话,也许你们看不懂,但绝对能让我回忆起那段快乐、轻松的时光,也就是在那里,我从战术型思维转入战略型思维,这是过后一些还在那里工作的兄弟们给的评价,慢慢自已发现好像是有这样的变化。

人得学会感恩,因为你感恩,所以你能够拥抱一切,因为你乐于感恩,所以一切也会拥抱你。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
10062 0
iOS - OC NSData 数据
前言 @interface NSData : NSObject @interface NSMutableData : NSData NSData 和它的可变长子类 NSMutableData 是字节缓冲区的对象化封装。
803 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
12070 0
阿里云服务器安全组设置内网互通的方法
虽然0.0.0.0/0使用非常方便,但是发现很多同学使用它来做内网互通,这是有安全风险的,实例有可能会在经典网络被内网IP访问到。下面介绍一下四种安全的内网互联设置方法。 购买前请先:领取阿里云幸运券,有很多优惠,可到下文中领取。
11817 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
13875 0
iOS - OC PList 数据存储
前言 直接将数据写在代码里面,不是一种合理的做法。如果数据经常改,就要经常翻开对应的代码进行修改,造成代码扩展性低。因此,可以考虑将经常变的数据放在文件中进行存储,程序启动后从文件中读取最新的数据。如果要变动数据,直接修改数据文件即可,不用修改代码。
810 0
+关注
悟静
从事软件行业近二十年
690
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载