iOS开发UI篇—字典转模型
一、能完成功能的“问题代码”
1.从plist中加载的数据
2.实现的代码
@property (nonatomic, strong) NSArray * - (NSArray * (! NSBundle *bundle = NSString *path = [bundle pathForResource: _appList = NSLog( - ( totalCol = CGFloat viewW = CGFloat viewH = CGFloat marginX = (self.view.bounds.size.width - totalCol * viewW) / (totalCol + CGFloat marginY = CGFloat startY = ( i = ; i < self.appList.count; i++ row = i / col = i % CGFloat x = marginX + (viewW + marginX) * CGFloat y = startY + marginY + (viewH + marginY) * UIView *appView = NSDictionary *dict = UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(, , viewW, imageView.image = [UIImage imageNamed:dict[ imageView.contentMode = UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, imageView.bounds.size.height, viewW, label.text = dict[ label.font = [UIFont systemFontOfSize: label.textAlignment = UIButton *button = button.frame = CGRectMake(, , viewW - , [button setTitle: button.titleLabel.font= [UIFont systemFontOfSize: [button setBackgroundImage:[UIImage imageNamed: [button setBackgroundImage:[UIImage imageNamed:
3.实现效果
4.代码问题
在上述代码的第62,69行,我们是直接通过字典的键名获取plist中的数据信息,在viewController中需要直接和数据打交道,如果需要多次使用可能会因为不小心把键名写错,而程序并不报错。鉴于此,可以考虑把字典数据转换成一个模型,把数据封装到一个模型中去,让viewController不再直接和数据打交道,而是和模型交互。
一般情况下,设置数据和取出数据都使用“字符串类型的key”,编写这些key时,编辑器没有智能提示,需要手敲。如:
dict[@"name"] = @"Jack";
NSString *name = dict[@"name"];
手敲字符串key,key容易写错
Key如果写错了,编译器不会有任何警告和报错,造成设错数据或者取错数据
二、字典转模型
1.字典转模型介绍
示意图:
字典转模型的好处:
(1)降低代码的耦合度
(2)所有字典转模型部分的代码统一集中在一处处理,降低代码出错的几率
(3)在程序中直接使用模型的属性操作,提高编码效率
(4)调用方不用关心模型内部的任何处理细节
字典转模型的注意点:
模型应该提供一个可以传入字典参数的构造方法
- (instancetype)initWithDict:(NSDictionary *)dict;
+ (instancetype)xxxWithDict:(NSDictionary *)dict;
提示:在模型中合理地使用只读属性,可以进一步降低代码的耦合度。
2.代码示例(一)
新建一个类,用来作为数据模型
viewController.m文件代码(字典转模型)
@property (nonatomic, strong) NSArray * - (NSArray * (! NSBundle *bundle = NSString *path = [bundle pathForResource: NSArray *array = NSMutableArray *arrayM = (NSDictionary *dict _appList = - ( totalCol = CGFloat viewW = CGFloat viewH = CGFloat marginX = (self.view.bounds.size.width - totalCol * viewW) / (totalCol + CGFloat marginY = CGFloat startY = ( i = ; i < self.appList.count; i++ row = i / col = i % CGFloat x = marginX + (viewW + marginX) * CGFloat y = startY + marginY + (viewH + marginY) * UIView *appView = LFAppInfo *appInfo = UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(, , viewW, imageView.image = imageView.contentMode = UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, imageView.bounds.size.height, viewW, label.text = label.font = [UIFont systemFontOfSize: label.textAlignment = UIButton *button = button.frame = CGRectMake(, , viewW - , [button setTitle: button.titleLabel.font= [UIFont systemFontOfSize: [button setBackgroundImage:[UIImage imageNamed: [button setBackgroundImage:[UIImage imageNamed: button.tag = - ()downloadClick:(UIButton * NSLog( UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(, , , label.textAlignment = label.backgroundColor = LFAppInfo *appInfo = label.text = [NSString stringWithFormat: label.font = [UIFont systemFontOfSize: label.alpha = [UIView animateWithDuration: animations:^ label.alpha = } completion:^
模型.h文件代码
<Foundation/Foundation.h> @property (nonatomic, copy) NSString * @property (nonatomic, copy) NSString * @property (nonatomic, strong, ) UIImage * - (instancetype)initWithDict:(NSDictionary * + (instancetype)appInfoWithDict:(NSDictionary *
模型.m文件数据处理代码
UIImage * - (instancetype)initWithDict:(NSDictionary * self = self.name = dict[ self.icon = dict[ + (instancetype)appInfoWithDict:(NSDictionary * - (UIImage * (! _imageABC =
3.代码示例(二)
数据信息:plist文件
字典转模型(初步)
模型.h文件
<Foundation/Foundation.h> @property (nonatomic, copy) NSString * @property (nonatomic, copy) NSString * @property (nonatomic, copy) NSString * @property (nonatomic, strong) NSArray * @property (nonatomic, strong) UIImage * - (instancetype)initWithDict:(NSDictionary * + (instancetype)questionWithDict:(NSDictionary *
模型.m文件
+ (instancetype)questionWithDict:(NSDictionary * - (instancetype)initWithDict:(NSDictionary * self = self.answer = dict[ self.icon = dict[ self.title = dict[ self.options = dict[ }
viewController.m文件中的数据处理
- (NSArray * (! NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource: NSMutableArray *arrayM = (NSDictionary *dict _questions= }
字典转模型(优化)
上面代码可以做进一步的优化,从plist文件中读取数据是可以交给模型去处理的,优化后代码如下:
模型.h文件
<Foundation/Foundation.h> @property (nonatomic, copy) NSString * @property (nonatomic, copy) NSString * @property (nonatomic, copy) NSString * @property (nonatomic, strong) NSArray * @property (nonatomic, strong) UIImage * - (instancetype)initWithDict:(NSDictionary * + (instancetype)questionWithDict:(NSDictionary * + (NSArray *
模型.m文件
+ (instancetype)questionWithDict:(NSDictionary * - (instancetype)initWithDict:(NSDictionary * self = self.answer = dict[ self.icon = dict[ self.title = dict[ self.options = dict[ + (NSArray * NSArray *array = [NSArray arrayWithContentsOfFile:[[NSBundle mainBundle] pathForResource: NSMutableArray *arrayM = (NSDictionary *dict
viewController.m文件中的数据处理代码部分
- (NSArray * (! _questions = }
补充内容:(KVC)的使用
(1)在模型内部的数据处理部分,可以使用键值编码来进行处理
- (instancetype)initWithDict:(NSDictionary * self = [self setValue:dict[] forKeyPath: [self setValue:dict[] forKeyPath: [self setValue:dict[] forKeyPath: [self setValue:dict[] forKeyPath: }
(2)setValuesForKeys的使用
上述数据操作细节,可以直接通过setValuesForKeys方法来完成。
- (instancetype)initWithDict:(NSDictionary * self = }
三、补充说明
1.readonly属性
(1)@property中readonly表示不允许修改对象的指针地址,但是可以修改对象的属性。
(2)通常使用@property关键字定义属性时,会生成getter&setter方法,还会生成一个带下划线的成员变量。
(3)如果是readonly属性,只会生成getter方法,不会生成带下划线的成员变量.
2.instancetype类型
(1)instancetype会让编译器检查实例化对象的准确类型
(2)instancetype只能用于返回类型,不能当做参数使用
3.instancetype & id的比较
(1) instancetype在类型表示上,跟id一样,可以表示任何对象类型
(2) instancetype只能用在返回值类型上,不能像id一样用在参数类型上
(3) instancetype比id多一个好处:编译器会检测instancetype的真实类型