以最少的代码让自定的model实现NSCoding、NSCopying协议

项目中用到了自定义的model:Person(栗子)。此model需要可以实现归档的功能,但是属性非常多,且类似的model很多。如果按照常规去写归档的代码,那么无论是写起来还是维护起来都非常困难。

由于model继承自NSObject,所以给NSObject添加了扩展用来实现自定义model的归档功能。实现思路来源于伟大的网络和MJExtention,所以应该不算是原创吧,反正这个实现也快烂大街了。

大致为:

1:获取当前类及父类的class及属性名称和类型

2:实现归档常规方法

3:宏定义常规方法,方便实现

最主要的方法为第一步。

NSObject (Coding).m

- (void)coding_encode:(NSCoder *)aCoder {
    //获取当前类属性名称及值
    [self enumPropertyList:^(id key, id value) {
        //归档常规方法
        [aCoder encodeObject:value forKey:key];
    }];
}
- (nullable instancetype)coding_decode:(NSCoder *)aDecoder {
    [self enumPropertyList:^(id key, id value) {

        [self setValue:[aDecoder decodeObjectForKey:key] forKey:key];
    }];

    return self;
}
- (void)enumPropertyList:(void(^)(id key, id value))emunBlock {

    //获取当前类及父类
    [self enumClass:^(__unsafe_unretained Class cl, BOOL *stop) {
        //根据获取的类的名称得到所有属性相关信息
        [self propertyForClass:cl finish:^(PropertyModel *pModel) {
            NSString *attributeTypeString = pModel.propertyType;//此处为了方便只获取了属性名称 使用PropertyModel便于扩展
            NSString *name = pModel.name;
            //判断当前属性是否支持coding协议
            if ([attributeTypeString hasPrefix:@"@\""]) {//对象类型都是以@"开头
                attributeTypeString = [attributeTypeString substringWithRange:NSMakeRange(2, attributeTypeString.length - 3)];

                Class attributeClass = NSClassFromString(attributeTypeString);

                BOOL isConformCoding = class_conformsToProtocol(attributeClass, NSProtocolFromString(@"NSCoding"));
                NSString *message = [NSString stringWithFormat:@"model:%@ 不支持NSCoding协议",attributeTypeString];
                NSAssert(isConformCoding, message);
            }

            if (emunBlock) {
                emunBlock(name,[self valueForKey:name]);
            }
        }];
    }];
}

获取类名及属性名称

NSObject (Class).m

- (void)enumClass:(void(^)(Class cl, BOOL *stop))enumBlock {
    if (!enumBlock) {
        return;
    }
    BOOL sstop = NO;

    Class c = self.class;

    while (c && !sstop) {

        enumBlock(c,&sstop);

        c = class_getSuperclass(c);

        if (isClassForFoundatation(c)) {
            break ;
        }
    }

}

此处借鉴(copy)了MJExtention实现方式,包括判断当前类是否属于Foundataion类型的方法,只不过我使用了函数的方式表示,纯粹是想尝试一下不同的风格。

NSSet *foundationClasses(){
    return [NSSet setWithObjects:
            [NSURL class],
            [NSDate class],
            [NSValue class],
            [NSData class],
            [NSError class],
            [NSArray class],
            [NSDictionary class],
            [NSString class],
            [NSAttributedString class],
            nil];

}

BOOL isClassForFoundatation(Class class){
    __block BOOL result = NO;
    [foundationClasses() enumerateObjectsUsingBlock:^(id  _Nonnull obj, BOOL * _Nonnull stop) {

        if ([class isSubclassOfClass:obj] || (class == [NSObject class])) {
            result = YES;
            *stop = YES;
        }
    }];

    return result;
}

以下是根据class获取属性

- (void)propertyForClass:(Class)cl finish:(void(^)(PropertyModel *pModel))finish {
    if (!finish) {
        return;
    }

    unsigned int count;

    objc_property_t *properties = class_copyPropertyList(cl, &count);

    for (int i = 0; i < count; i++) {
        objc_property_t p = properties[i];
        NSString *name = @(property_getName(p));

        NSString *attribute = @(property_getAttributes(p));
        NSRange dotLocation = [attribute rangeOfString:@","];
        NSString *attributeTypeString ;
        if (dotLocation.location == NSNotFound) {
            attributeTypeString = [attribute substringFromIndex:1];
        }else{
            attributeTypeString = [attribute substringWithRange:NSMakeRange(1, dotLocation.location - 1)];
        }

        PropertyModel *model = [PropertyModel new];
        model.name = name;
        model.propertyType = attributeTypeString;

        finish(model);
    }
    free(properties);

}

其中PropertyModel是为了方便扩展,当前是需要属性名称及类型。

@interface PropertyModel : NSObject

@property (nonatomic, copy) NSString *name;

@property (nonatomic, copy) NSString *propertyType;

@end

到此,核心代码已经完毕

在.h文件中声明宏定义就可以

@interface NSObject (Coding)
- (void)coding_encode:(NSCoder *_Nonnull)aCoder ;
- (nullable instancetype)coding_decode:(NSCoder *_Nonnull)aDecoder ;
@end

#define CodingImplmentation - (void)encodeWithCoder:(NSCoder *)aCoder {     [self coding_encode:aCoder];    }  - (nullable instancetype)initWithCoder:(NSCoder *)aDecoder {     if (self == [super init]){         [self coding_decode:aDecoder];    }    return  self;}

#define DWObjectCodingImplmentation CodingImplmentation

在自定义的model:Person.m的文件中只添加宏定义就可以自动实现归档功能

@implementation Person

#pragma mark - 归档

DWObjectCodingImplmentation

PS:不要忘记在.h文件中写上<NSCoding>

demo验证了继承自Person的model可以归档、model中属性嵌套model也可以正常运行。

献上github源码,下载下来直接使用就可以。想了一下是否要支持cocoapod或者carthage,但是代码这么简单,还是算了。

如果有任何不符合规范或者遗漏的地方,请各路大神指教。

github: https://github.com/DawnWdf/NSObjectExtention

PPS:另外代码中有实现copying协议的方法,与coding类似,不赘述。可变对象的copy协议,由于但是没什么可用的地方所以注释了。有兴趣的可以自己尝试。

还有字典转换model的方法,纯粹自娱自乐,请大家忽略。

时间: 2024-10-10 10:45:09

以最少的代码让自定的model实现NSCoding、NSCopying协议的相关文章

app分享功能,微信分享代码,几行代码轻松搞定

让你的应用支持分享送积分功能,获得更多社交流量. *通过友推,开发者用几行代码就可以为应用添加分享送积分功能,并提供详尽的统计报表 *除了本身具备的分享功能外,开发者也可将积分功能单独集成在已有分享组件的APP上, 让您的应用更多地通过用户的分享推荐触达新用户,获得更多社交流量. 一.分享组件功能 1.支持微信,QQ,新浪微博,QQ空间,短信,邮件等多家大型社交媒体平台一键分享2.支持积分抽奖活动在线活动创建3.后台多维度数据统计用户分享行为以及其他数据,让您及时做出精准的营销定位策略4.集成简

一行代码轻松搞定各种IE兼容问题,IE6,IE7,IE8,IE9,IE10

在网站开发中不免因为各种兼容问题苦恼,针对兼容问题,其实IE给出了解决方案Google也给出了解决方案百度也应用了这种方案去解决IE的兼容问题? 百度源代码如下: 1 <!Doctype html> 2 <html xmlns=http://www.w3.org/1999/xhtml xmlns:bd=http://www.baidu.com/2010/xbdml> 3 <head> 4 <meta http-equiv=Content-Type content=

最少javascript代码完成一个2048游戏

原生javascript代码写的2048游戏.建议在谷歌浏览器下跑.'WASD'控制方向.演示地址请移步:http://runjs.cn/detail/bp8baf8b 直接贴代码~ html: <!DOCTYPE> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/htm

嗯嗯,一句代码就搞定 RecycleView 侧滑菜单、添加头部底部、加载更多

很早就萌生了将这种方案封装为一个开源库的想法,旨在实现调用方式最简单,且又不失可定制性.本库最大的特点的是采用了 Glide 简洁明了的链式调用方式,一句代码即可添加侧滑菜单.头部底部等. 特性: 1.自定义侧滑菜单布局 2.添加头部.底部 3.轻松实现加载更多 4.设置 item 间距 5.多种 item 类型 6.支持 LinearLayout 及 GridLayout 7.一句代码实现所有功能 效果: 左侧滑菜单.右侧滑菜单.自定义菜单布局:      头部.多头部:      底部.多底

SwipeRefreshLayout,用最少的代码定制最美的上下拉刷新样式

下拉刷新框架其实有很多,而且质量都比较高.但是在日常开发中,每一款产品都会有一套自己独特的一套刷新样式.相信有很多小伙伴在个性化定制中都或多或少的遇到过麻烦.今天我就给大家推荐一个在定制方面很出彩的一个刷新框架SwipeToLoadLayout,该框架自身完成了下拉刷新与上拉加载功能,同时将顶部视图与底部视图的UI定制功能通过接口很方便的提供给使用者自行定义.相关代码已经上传到github上,欢迎star.fork 基本流程 先简单了解一下SwipeToLoadLayout的使用流程,以下拉刷新

数据预处理速度高倍提升,3行python代码简单搞定!

Python 是机器学习领域内的首选编程语言,它易于使用,也有很多出色的库来帮助你更快处理数据.但当我们面临大量数据时,一些问题就会显现-- 目前,大数据(Big Data)这个术语通常用于表示包含数十万数据点的数据集.在这样的尺度上,工作进程中加入任何额外的计算都需要时刻注意保持效率.在设计机器学习系统时,数据预处理非常重要--在这里,我们必须对所有数据点使用某种操作. 在默认情况下,Python 程序是单个进程,使用单 CPU 核心执行.而大多数当代机器学习硬件都至少搭载了双核处理器.这意味

微信自动回复,Python几行代码就搞定了,消息不在错过

之前写过一篇python-requests获取好友列表的文章,简直花费了好多的时间和精力,又抓包,又找参数,又分析的,简直麻烦透顶,今天突然知道了另外一种捷径,几行代码就可以完成.... 学习Python中有不明白推荐加入交流裙                                           号:735934841                                           群里有志同道合的小伙伴,互帮互助,                     

【制作表情包】Python拆分和合并GIF动态图(几行代码就搞定)

“表情包”是当前社交软件上不可或缺的交流方式,难以用文字表达的意思,发一个“表情包”,对方就能心领神会.下面是小派制作的一个表情包,准确地讲,是在已有表情包的基础上,二次加工而成的. 下面以最简单的代码形式(10行左右),介绍上述“表情包”的制作过程.第一,将GIF动态图拆分成图形帧.下图是网络上找到的一个GIF格式动态图. 利用Python将上述GIF格式动态图拆分图形帧,只需要输入以下代码.其中第1-2行是导入os库.从PIL库中导入Image函数功能.第3行是Image.open打开位于D

几行代码轻松搞定python的sqlite3的存取

很简单: 存数据: 1.加载sqlite3驱动(只需一行代码) 2.用驱动执行查询语句(只需一行代码) 取数据: 1.加载sqlite3驱动(只需一行代码) 2.用驱动执行查询语句(只需一行代码) 乍一看,sqlite存取数据方式似乎都一样,实际上,就是一样,废话不多说 上例子: 1.存以下内容: s=['Alice','Joker','张三','王五'] 2.先建立数据库,再在数据库中建一个数据表: conn=sqlite3.connect('SqliteDatebase.db') #建立数据