动态获得类的属性来构建模型
应用场景: 有时候我们从服务器端拿来的Json数据,其中包含很多的属性,而我们在转模型时,模型需要的属性并没有那么多,这种情况下,如果直接用KVC转模型结果会导致程序崩溃,以下是两种解决方法:
简单版
- 模型文件
//YFNews.h
#import <UIKit/UIKit.h>
@interface YFNews : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *digest;
@property (nonatomic, copy) NSString *imgsrc;
@property (nonatomic, copy) NSString *ptime;
+ (instancetype)newsWithDict:(NSDictionary *)dict;
@end
//YFNews.m
#import "YFNews.h"
@interface YFNews()
@end
@implementation YFNews
+ (instancetype)newsWithDict:(NSDictionary *)dict {
//创建模型
id obj = [[self alloc] init];
//这里要列出所有本模型中需要的属性
NSArray *properties = @[@"title",@"disgest",@"imgsrc",@"ptime"];
for (NSString *property in properties) {
if (dict[property]) { //根据指定的属性给构建模型
[obj setValue:dict[property] forKey:property];
}
}
return obj;
}
@end
主控制器文件
//ViewController.h
#import <UIKit/UIKit.h>
@interface ViewController : UIViewController
@end
//ViewController.m
#import "ViewController.h"
#import "YFNews.h"
@interface ViewController ()
// 新闻数据
@property (nonatomic, strong) NSMutableArray *news;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
[self loadData];
}
- (void)loadData{
// URL
NSURL *url = [NSURL URLWithString:@"http://c.m.163.com/nc/article/headline/T1348647853363/0-20.html"];
// Request
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
// 发送请求
[NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse * _Nullable response, NSData * _Nullable data, NSError * _Nullable connectionError) {
NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:NULL];
// NSLog(@"%@",dict);
// 获得字典的键的迭代器对象
NSEnumerator *enumerator = dict.keyEnumerator;
// 获得rootKey
NSString *rootKey = enumerator.nextObject; //T1348647853363
// NSLog(@"%@",rootKey);
// 获得新闻数据
NSArray *newsArray = dict[rootKey]; //是一个保存字典对象的数组
self.news = [NSMutableArray array];
// 逐个取字典来转模型
for (NSDictionary *dict in newsArray) {
YFNews *news = [YFNews newsWithDict:dict];
[self.news addObject:news];
}
NSLog(@"self.news = %@",self.news);
}];
}
@end
简单版的弊端在于模型属性的增添需要对那个保存模型属性的数组进行修改.
动态获取版
- 工具类:
//NSObject+Extend.h
#import <Foundation/Foundation.h>
@interface NSObject (Extend)
/**
* 获得当前类的所有属性的数组
*/
+ (NSArray *)properties;
@end
//NSObject+Extend.m
#import "NSObject+Extend.h"
#import <objc/runtime.h>
@implementation NSObject (Extend)
/**
* 获得当前类的所有属性
*/
+ (NSArray *)properties {
NSMutableArray *pArray = [NSMutableArray array];
unsigned int count = 0;
// 获得指定类的所有属性
/**
*
* @param class] 哪个类
* @param count 存放属性的个数
*
* @return 数组首地址
*/
objc_property_t *properties = class_copyPropertyList([self class], &count);
// class_copyIvarList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>) //获得当前类所有成员变量
// class_copyMethodList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>) //获得当前类的所有方法
// class_copyPropertyList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)//获得当前类的所有属性
// class_copyProtocolList(<#__unsafe_unretained Class cls#>, <#unsigned int *outCount#>)//获得当前类的所有协议
// 遍历属性
for (int index = 0; index < count; ++index) {
// 根据索引获得对应的属性(属性是一个结构体,包含很多其他的信息)
objc_property_t property = properties[index];
// 获得属性名字
const char *cname = property_getName(property);
// 将c语言字符串转换为oc字符串
NSString *ocname = [[NSString alloc] initWithCString:cname encoding:NSUTF8StringEncoding];
[pArray addObject:ocname];
}
NSLog(@"pArray= %@",pArray);
return pArray;
}
@end
- 模型文件:
//YFNews.h
#import <UIKit/UIKit.h>
@interface YFNews : NSObject
@property (nonatomic, copy) NSString *title;
@property (nonatomic, copy) NSString *digest;
@property (nonatomic, copy) NSString *imgsrc;
@property (nonatomic, copy) NSString *ptime;
@property (nonatomic, copy) NSString *hehe;
@property (nonatomic, copy) NSString *ename;
+ (instancetype)newsWithDict:(NSDictionary *)dict;
@end
//YFNews.m
#import "YFNews.h"
#import "NSObject+Extend.h"
@interface YFNews()
@end
@implementation YFNews
+ (instancetype)newsWithDict:(NSDictionary *)dict {
//创建模型
id obj = [[self alloc] init];
//这里调用了工具类中的类方法,使用运行时返回属性数组
NSArray *properties = [self properties];
//遍历属性数组,初始化模型
for (NSString *property in properties) {
if (dict[property]) {
[obj setValue:dict[property] forKey:property];
}
}
//返回模型
return obj;
}
@end
注意: 主控制器文件和上面的简单版一样.