前一段时间学习了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转换库的更多相关文章

  1. iOS 对象和json互相转换

    // 将字典或者数组转化为JSON串 - (NSData *)toJSONData:(id)theData { NSError *error = nil; NSData *jsonData = [NS ...

  2. iOS中JSON解析三方库的比较

    网络数据解析框架 1.  JsonModel 一个 JSON 模型转换库,有着比较简洁的接口.Model 需要继承自 JSONModel. 2.  yyModel yyModel比较轻量(算上.h 只 ...

  3. iOS 字典转模型Model

    基本原理 利用 runtime 原理,获取模型中所有实例变量列表,根据实例变量以此获取模型中成员变量的名称和属性类型,区分Foundation和自定义属性,需要对NSDictionary和NSArra ...

  4. Java对象与JSON互相转换jsonlib以及手动创建JSON对象与数组——(二)

    首先声明一下,jsonlib转换与GSON相比太差劲了,操作不是一般的繁琐.GSON可以直接转换成各种集合与对象类型.强烈推荐使用GSON.而且GSON一个方法就可以解决,jsonlib转来转去太繁琐 ...

  5. iOS:iOS开发非常全的三方库、插件等等

    iOS开发非常全的三方库.插件等等 github排名:https://github.com/trending, github搜索:https://github.com/search. 此文章转自git ...

  6. iOS、mac开源项目及库汇总

    原文地址:http://blog.csdn.net/qq_26359763/article/details/51076499    iOS每日一记------------之 中级完美大整理 iOS.m ...

  7. iOS的非常全的三方库,插件,大牛博客

    转自: http://www.cnblogs.com/zyjzyj/p/6015625.html github排名:https://github.com/trending, github搜索:http ...

  8. iOS开发 非常全的三方库、插件、大牛博客等等

    UI 下拉刷新 EGOTableViewPullRefresh- 最早的下拉刷新控件. SVPullToRefresh- 下拉刷新控件. MJRefresh- 仅需一行代码就可以为UITableVie ...

  9. iOS基础 - XML & JSON

    一.HTML & XML HTML 是用来描述网页的一种语言 HTML 指的是超文本标记语言 (Hyper Text Markup Language) HTML 不是一种编程语言,而是一种标记 ...

随机推荐

  1. c#中单元测试

    从走进.net后发现每天有写不完的代码,有做不完的测试...人感觉都已经机械,我们需要认清自己调整好心态,问下自己是否真的喜欢编程.我的答案当然也就是我爱编码,编码给我带来了许多欢乐,每天都给我体验小 ...

  2. hbase shell下如何使用删除键

    今天刚安装好了hbase,通过Secure CRT登录hbase shell,敲入错误命令无法使用删除键(Backspace或是Ctrl+Backspace都不管用)删除,后来在终端-->仿真下 ...

  3. jquery日历datepicker的使用方法

    jquery.ui.datepicker.js 用法: http://blog.csdn.net/zb0567/article/details/7906238   原文  http://blog.cs ...

  4. JPEG最优压缩参数试验【光影魔术手VS Image Optimizer】

                                          样本数量:100张(1MB-2.6MB)旅游照     样本大小:157MB 156.44      样本尺寸:3M(204 ...

  5. 第一个GTK+程序

    在这一章节中,我们将开始编写第一个GTK+程序. 超级简单的例子 我们要“制造”一个超级简单的GTK+程序.就是显示一个空白的窗口. #include <gtk/gtk.h> int ma ...

  6. 《C Primer Plus 第五版》读书笔记

    CH1-2:概述 链接器:链接库代码.启动代码(start-up code) CH3-5:数据.字符串.运算符 1 数据类型存储方式:整数类型.浮点数类型 2 浮点数存储:小数部分+指数部分 3 in ...

  7. 面向对象(class0420)

    测试 交换两个变量的值 int num1 = 5;int num2=6; 通过程序交换让num1 = 6,num2=5; 求两个数的最大值 (求三个数最大值) 求1-100之间所有奇数的和 找胖子,{ ...

  8. 【noip2005】篝火晚会

    题解: 首先我们要知道一个性质: 把长度为n的序列变成目标序列最多需要n个操作 证明1: 我们可以将原序列上每位上的数字向目标序列相同位置的数字连一条有向边 如: 原序列:   1 2 3 目标序列: ...

  9. acm-DP整理

    一.背包 .各种01背包 void leastOne_Pack(int id, int num ) {//至少取一个: int i, j, c, v ; ; i <= num ; i ++ ) ...

  10. 远程控制篇:用Delphi模拟键盘输入/鼠标点击

    模拟键盘我们用Keybd_event这个api函数,模拟鼠标按键用mouse_event函数. Keybd_event函数能触发一个按键事件,也就是会产生一个WM_KEYDOWN或WM_KEYUP消息 ...