发布于 2016-04-10 15:49:50 | 232 次阅读 | 评论: 0 | 来源: 分享

这里有新鲜出炉的精品教程,程序狗速度看过来!

iOS苹果移动操作系统

苹果iOS是由苹果公司开发的移动操作系统。苹果公司最早于2007年1月9日的Macworld大会上公布这个系统,最初是设计给iPhone使用的,后来陆续套用到iPod touch、iPad以及Apple TV等产品上。


前一段时间学习了Runtime,对类和对象的结构,和一些消息转发有一些自己的理解,现在希望简单的应用下,就决定自己写一个简单的JSON与Model的相互转化,现在总结下。

建议查看

观察下面这个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学习笔记

在这个消息发送过程中,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的理解。就当自己的一个小练习,后续会继续维护,让它对更多类型进行支持。代码结构上可能不是那么好,后续会将整体的结构重新设计下,增加可读性,也欢迎来提出建议。



最新网友评论  共有(0)条评论 发布评论 返回顶部

Copyright © 2007-2017 PHPERZ.COM All Rights Reserved   冀ICP备14009818号  版权声明  广告服务