iOS 手动打造JSON Model转换库
前一段时间学习了Runtime,对类和对象的结构,和一些消息转发有一些自己的理解,现在希望简单的应用下,就决定自己写一个简单的JSON与Model的相互转化,现在总结下。
建议查看
参考资料 :Runtime学习笔记
http://lastdays.cn/2016/02/22/runtime/
项目地址: LYModelData
https://github.com/MrLoong/LYModelData
观察下面这个JSON数据和Model数据
NSString *girlFriend = @"白菜";
id parmenters = @{
@"girlFriend":girlFriend,
@"age":@22.1,
@"name":@"Lastdays",
@"time":@"2016-03-18 5:55:49 +0000"
};
@interface Model : NSObject
@property NSNumber *age;
@property NSString *name;
@property NSString *girlFriend;
@property NSData *time;
@end
开始的时候仔细想了一下,如何能够动态的去添加属性值,并且根据对应的属性进行赋值,还要保证类型正确,这是我最开始考虑的问题。但是最核心问题就是动态实现。
我们一步一步来解决问题,首先我们先获取Model属性,取得Model的一些信息
获取Model属性
runtime提供了class_copyPropertyList来获取属性列表,OK,我们可以来看一下用它获取的数据是什么样的?查看runtime源码
/***********************************************************************
* class_copyPropertyList. Returns a heap block containing the
* properties declared in the class, or nil if the class
* declares no properties. Caller must free the block.
* Does not copy any superclass's properties.
**********************************************************************/
objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)
{
old_property_list *plist;
uintptr_t iterator = 0;
old_property **result = nil;
unsigned int count = 0;
unsigned int p, i;
if (!cls) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(classLock);
iterator = 0;
while ((plist = nextPropertyList(cls, &iterator))) {
count += plist->count;
}
if (count > 0) {
result = (old_property **)malloc((count+1) * sizeof(old_property *));
p = 0;
iterator = 0;
while ((plist = nextPropertyList(cls, &iterator))) {
for (i = 0; i < plist->count; i++) {
result[p++] = property_list_nth(plist, i);
}
}
result[p] = nil;
}
if (outCount) *outCount = count;
return (objc_property_t *)result;
}
typedef struct old_property *objc_property_t;
struct old_property {
const char *name;
const char *attributes;
};
```
从上面的三段runtime源码中,课本上就能判断出,其实返回结果就是一些old_property,并且每个old_property中含有对应的name和其他信息。
总结起来说就是**class_copyPropertyList**获取Model属性列表,属性列表里面的objc_property_t包含着这个属性的类型和名字等一些信息。
根据刚才的分析设计出以下结构:
``` bash
-(id)modelToJsonObject:(NSObject *)model{
Class cls = self.class;
unsigned int countProperty = 0;
objc_property_t *propertys = class_copyPropertyList(cls,&countProperty);
NSMutableDictionary *dic = [NSMutableDictionary new];
for (unsigned int i = 0; i<countProperty; i++) {
PropertyInfo *propertyInfo = [[PropertyInfo alloc] initWithProperty:propertys[i]];
if (propertyInfo.propertyName!=nil) {
dic[propertyInfo.propertyName] = [self LYModelSetJsonObjectWith:model propertyInfo:propertyInfo];
}
}
return dic;
}
PropertyInfo也就是属性信息,我们将Model的所有属性存放到NSMutableDictionary中,key就是属性名,Value就是PropertyInfo。
接下来开始获取Model的属性信息PropertyInfo
我们可以通过property_getName来获取属性名,查看源码
const char *property_getName(objc_property_t prop)
{
return oldproperty(prop)->name;
}
接下来就是获取属性的类型和一些其他的信息。获取属性的信息其实和上面的原理差不多,我们使用property_copyAttributeList,查看下它的源码
objc_property_attribute_t *property_copyAttributeList(objc_property_t prop,
unsigned int *outCount)
{
if (!prop) {
if (outCount) *outCount = 0;
return nil;
}
mutex_locker_t lock(classLock);
return copyPropertyAttributeList(oldproperty(prop)->attributes,outCount);
}
看到这里,不往下继续分析源码了,其实可以看到,attributes就是我们想要的信息,其实每个property也是有自己对应的attributes。
这个attributes是什么样呢?翻看源码,找到了答案
typedef struct {
const char *name;
const char *value;
} objc_property_attribute_t;
加一下断点,看看
可以看到,name是T,Value是NSNumber,我们来获取下NSNumber这个属性类型。
for (unsigned int i = 0; i<attrCount; i++) {
if (attrs[i].name[0] == 'T') {
size_t len = strlen(attrs[i].value);
if (len>3) {
char name[len - 2];
name[len - 3] = '';
memcpy(name, attrs[i].value + 2, len - 3);
_typeClass = objc_getClass(name);
}
}
}
基本上我们想要的信息基本上都已经获取到了,现在接下来就是做动态设定。
中间做个插曲简单的说下Objc是动态语言,[receiver message]的执行过程当中,[receiver message]是会被动态编译的,Objc是动态语言,因此它会想尽办法将编译连接推迟到运行时来做。runtime这个时实运行系统就是来执行编译后的代码。想详细了解,欢迎阅读Runtime学习笔记
http://lastdays.cn/2016/02/22/runtime/
在这个消息发送过程中,objc_msgSend充当着很重要的角色,所以我们可以主动触发objc_msgSend,来模拟getter,setter方法获取属性值,或者建立。
我们通过SEL来定义选择器,选择器是什么?就是方法名的唯一标识符
根据刚才的想法,编写的代码最后是这个样子
-(instancetype)initWithProperty:(objc_property_t)property{
_property = property;
const char *name = property_getName(property);
if (name) {
_propertyName = [NSString stringWithUTF8String:name];
}
unsigned int attrCount;
objc_property_attribute_t *attrs = property_copyAttributeList(property, &attrCount);
for (unsigned int i = 0; i<attrCount; i++) {
if (attrs[i].name[0] == 'T') {
size_t len = strlen(attrs[i].value);
if (len>3) {
char name[len - 2];
name[len - 3] = '';
memcpy(name, attrs[i].value + 2, len - 3);
_typeClass = objc_getClass(name);
}
}
}
NSString *setter = [NSString stringWithFormat:@"set%@%@:", [_propertyName substringToIndex:1].uppercaseString, [_propertyName substringFromIndex:1]];
_setter = NSSelectorFromString(setter);
_getter = NSSelectorFromString(_propertyName);
return self;
}
基本的准备工作,和一些问题都解决了,接下来可以写功能了。
JSON转Model
根据刚才说的,我们可以主动触发objc_msgSend,来模拟setter方法建立属性值。设计出以下方法
-(void)LYModelSetPropertyWithModel:(id) model value:(id)value propertyInfo:(PropertyInfo *) propertyInfo{
((void (*)(id, SEL, id))(void *) objc_msgSend)((id)model, propertyInfo.setter, value);
}
我们将Model的所有属性存放到NSMutableDictionary中,key就是属性名,Value就是PropertyInfo。
现在就可以动态设定了
-(BOOL)LYModelSelectProperties:(NSDictionary *)dictonary{
ClassInfo *cls = [[ClassInfo alloc]initWithClass:object_getClass(self)];
id key, value;
NSArray *keys = [dictonary allKeys];
NSUInteger count = [keys count];
for (int i = 0; i < count; i++){
key = [keys objectAtIndex: i];
value = [dictonary objectForKey: key];
if (cls.propertyInfo[key]) {
[self LYModelSetPropertyWithModel:self value:value propertyInfo:cls.propertyInfo[key]];
}
}
return YES;
}
完成动态设定
Model转JSON
原理跟JSON转Model
我们可以主动触发objc_msgSend,来模拟getter方法来获取属性值。
-(id)LYModelSetJsonObjectWith:(id)model propertyInfo:(PropertyInfo *)propertyInfo{
id value = ((id (*)(id, SEL))(void *) objc_msgSend)((id)model, propertyInfo.getter);
return value;
}
建立NSDictionary
-(id)modelToJsonObject:(NSObject *)model{
Class cls = self.class;
unsigned int countProperty = 0;
objc_property_t *propertys = class_copyPropertyList(cls,&countProperty);
NSMutableDictionary *dic = [NSMutableDictionary new];
for (unsigned int i = 0; i<countProperty; i++) {
PropertyInfo *propertyInfo = [[PropertyInfo alloc] initWithProperty:propertys[i]];
if (propertyInfo.propertyName!=nil) {
dic[propertyInfo.propertyName] = [self LYModelSetJsonWith:model propertyInfo:propertyInfo];
}
}
return dic;
}
完成获取
测试
NSString *girlFriend = @"白菜";
id parmenters = @{
@"girlFriend":girlFriend,
@"age":@22.1,
@"name":@"Lastdays",
@"time":@"2016-03-18 5:55:49 +0000"
};
Model *model = [Model LYModelWithJSON:parmenters];
NSLog(@"%@",model.girlFriend);
NSLog(@"%@",model.name);
NSLog(@"%@",model.age);
NSLog(@"%@",model.time);
NSLog(@"========================================");
NSDictionary *jsonObject= [model LYModelToJson];
NSLog(@"%@",jsonObject);
结果:
总结
简单的JSON Model转换库,关键点就是在于对runtime的理解。就当自己的一个小练习,后续会继续维护,让它对更多类型进行支持。代码结构上可能不是那么好,后续会将整体的结构重新设计下,增加可读性,也欢迎来提出建议。
iOS 手动打造JSON Model转换库的更多相关文章
- iOS 对象和json互相转换
// 将字典或者数组转化为JSON串 - (NSData *)toJSONData:(id)theData { NSError *error = nil; NSData *jsonData = [NS ...
- iOS中JSON解析三方库的比较
网络数据解析框架 1. JsonModel 一个 JSON 模型转换库,有着比较简洁的接口.Model 需要继承自 JSONModel. 2. yyModel yyModel比较轻量(算上.h 只 ...
- iOS 字典转模型Model
基本原理 利用 runtime 原理,获取模型中所有实例变量列表,根据实例变量以此获取模型中成员变量的名称和属性类型,区分Foundation和自定义属性,需要对NSDictionary和NSArra ...
- Java对象与JSON互相转换jsonlib以及手动创建JSON对象与数组——(二)
首先声明一下,jsonlib转换与GSON相比太差劲了,操作不是一般的繁琐.GSON可以直接转换成各种集合与对象类型.强烈推荐使用GSON.而且GSON一个方法就可以解决,jsonlib转来转去太繁琐 ...
- iOS:iOS开发非常全的三方库、插件等等
iOS开发非常全的三方库.插件等等 github排名:https://github.com/trending, github搜索:https://github.com/search. 此文章转自git ...
- iOS、mac开源项目及库汇总
原文地址:http://blog.csdn.net/qq_26359763/article/details/51076499 iOS每日一记------------之 中级完美大整理 iOS.m ...
- iOS的非常全的三方库,插件,大牛博客
转自: http://www.cnblogs.com/zyjzyj/p/6015625.html github排名:https://github.com/trending, github搜索:http ...
- iOS开发 非常全的三方库、插件、大牛博客等等
UI 下拉刷新 EGOTableViewPullRefresh- 最早的下拉刷新控件. SVPullToRefresh- 下拉刷新控件. MJRefresh- 仅需一行代码就可以为UITableVie ...
- iOS基础 - XML & JSON
一.HTML & XML HTML 是用来描述网页的一种语言 HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 不是一种编程语言,而是一种标记 ...
随机推荐
- Hibernate4.x之映射关系--多对多
n-n的关联关系分为以下两类: 1.单向n-n关联关系: n-n的关联必须使用连接表 与1-n映射类似,必须为set集合元素添加key子元素,指定CATEGORIES_ITEMS表中参照CATEGOR ...
- codeforces 676D Theseus and labyrinth BFS搜索
分析:一个n*m的矩阵,每个格子有12个状态,每次按一次,每个格子转90度,所以整个矩阵只有4种状态,然后爆搜就好了 #include <cstdio> #include <iost ...
- C#发送简单的HTTP POST请求给传统的ASP网页。
设计思路 创建HTTPWebRequest类的一个实例,设置这个对象的Method属性为"POST",ContentType属性为"application/x-/www- ...
- NOIP2005 篝火晚会
篝火晚会 (fire.pas/c/cpp) [问题描述] 佳佳刚进高中,在军训的时候,由于佳佳吃苦耐劳,很快得到了教官的赏识,成为了“小教官”.在军训结束的那天晚上,佳佳被命令组织同学们进行篝火晚会. ...
- BNUOJ-15505 Largest Rectangle in a Histogram DP
题目链接:http://www.bnuoj.com/bnuoj/problem_show.php?pid=15505 每个h[i]维护两个值l[i]和r[i],分别表示大于h[i]的左边最远距离和小于 ...
- HDU-4628 Pieces 搜索 | DP
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4628 数据不大,枚举本质.首先对枚举出回文串,然后用DP或者搜索,这里因为层数不多,用bfs比较好,或 ...
- SQL Where语句中AND与OR的计算次序 .
AND 用在where子句中,用来指示检索满足所有给定条件的行,而OR用在where子句中,用来指示检索匹配任一给定条件的行. Where子句中可包含任意数目的AND和OR操作符号,但是要注意在SQL ...
- Eclipse的DDMS File Explorer无法进入data目录解决方案
进入data目录需要root权限.所以,你的手机要有root才可以.然后做以下步骤就可以了 1.cmd进入platform-tools目录 2.adb shell 3.su(这里要看手机有没有请求ro ...
- DTD - XML Building Blocks
The main building blocks of both XML and HTML documents are elements. The Building Blocks of XML Doc ...
- PCB编辑快捷键
TAB: 调节属性PgUp: 放大PgDn: 缩小Delete: 删除Ctrl+C: 复制Ctrl+V: 粘贴Ctrl+X: 剪贴Ctrl+Z: 撤销Ctrl+Y: 取消撤销Ctrl+F: 查找Ctr ...