导语
view controller 通常是一个我的项目中最宏大的文件,因为它外面常常蕴含了不属于它的代码,同时这也使它成为代码中最难以重用的局部。所以为 view controller 瘦身,让其中的代码复用性更强,把相干代码放到正确的中央显得尤其重要。
iOS 开发交换技术群:563513413,不论你是大牛还是小白都欢送入驻,分享 BAT, 阿里面试题、面试教训,探讨技术,大家一起交流学习成长!
将 Data Source 和其余协定拆散
为 view controller 瘦身最无效的办法就是把 UITableViewDataSource 中的代码移动到相干的类中,具体的办法能够参阅《iOS 利用开发 扼要 TableView》中的相干实现。
而更进一步,不只是 TableView,这个办法能够扩大到其余的协定上,比方 UICollectionViewDataSource。如果在开发中抉择应用 UICollectionView 代替 UITableView 时,这个办法能够让你简直不必批改 viewController 中的任何货色,甚至能够让 Data Source 同时反对两个协定,给予了极大的便利性。
将弱业务逻辑移到 Model 中
注:markdown 对代码块的语法是开始和完结行都要增加:“`, 其中 ` 为 windows 键盘左上角
首先是代码,以下的代码是帮忙用户查找优先事项的列表:
-(void)loadPriorities
{
NSDate *now = [NSDate date];
NSString *formatString = @”startDate <= %@ AND endDate >= %@”;
NSPredicate *predicate = [NSPredicate predicateWithFormat:formatString, now, now];
NSSet *priorities = [self.user.priorities filteredSetUsingPredicate:predicate];
self.priorities = [priorities allObjects];
}
然而,如果把这些代码移动到 User 类中会让它变得更加清晰,这时 ViewController.m 中会是:
-(void)loadPriorities
{
self.priorities = [self.user currentPriorities];
}
而 User + Extensions.m 中则是:
-(NSArray *)currentPriorities
{
NSDate *now = [NSDate date];
NSString *formatString = @”startDate <= %@ AND endDate >= %@”;
NSPredicate *predicate = [NSPredicate predicateWithFormat:formatString, now, now];
return [[self.priorities filteredSetUsingPredicate:predicate] allObjects];
}
将这些代码移动的根本原因是因为 ViewController.m 是大部分业务逻辑的载体,自身代码的复杂度曾经很高,所以这类跟业务关联不大的代码比方日期转换、图像裁剪、设定过滤器等的操作能够拆散到各自的类中实现,一方面为 viewController 减负,另一方面也能增进代码的复用。
对于这个题目的翻译我斟酌了比拟久的工夫,因为在原文中是 “Move Domain Logic into the Model”,意为“把 畛域逻辑 移到 Model 中”。对于“畛域逻辑 ”一词我进行过讲究,大抵意思为“ 稳固的、不会扭转的逻辑关系 ”,同时在原文中也是应用了 NSPredicate 作为例子援用,而我认为其例子中的代码也是与业务相干的,只不过关联性不大,而且不会轻易改变,所以应用了“ 弱业务逻辑 ”一词代替了“ 畛域逻辑”一词。
把数据处理的逻辑移到服务层
一些代码可能没方法很无效的挪动到 model 中,然而这些代码却和 model 中的代码有清晰的关联,对于这种问题,能够应用 Store。比方在上面的代码中,viewController 须要实现从一个文件中获取一些数据,并对其进行操作:
-(void)readArchive
{
NSBundle *bundle = [NSBundle bundleForClass:[self class]];
NSURL *archiveURL = [bundle URLForResource:@”photodata” withExtension:@”bin”];
NSDate *data = [NSData dataWithContentsOfURL:archiveURL options:0 error:NULL];
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];
_users = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@”users”];
_photos = [unarchiver decodeObjectOfClass:[NSArray class] forKey:@”photos”];
[unarchiver finishDecoding];
}
事实上,view controller 不须要分明怎么实现这些货色,而应该将这些解决交给一个 store object 来实现。
通过对代码进行拆散,可能增进代码复用、对代码进行单元测试、放弃 view controller 整洁等。同时可能让 view controller 更多关注于业务自身的内容,把数据的读取、缓存、新建等操作交给服务层来解决。
把网络服务的逻辑移到 Model 层
这与下面提到的十分相似:不要把网络服务的逻辑放到 view controller 中,而应该把它们寄存到不同的类中。
对于 view controller,应该只是应用一个 completion block 来调用这些办法,而把网络申请、错误处理、缓存解决交给这些类来实现
把解决 view 的代码移到 view 层
无论是应用 Storyboard 还是纯代码编写 view,创立简单的 view 的工作不应该交给 view controller。
比方在须要创立一个日期选择器时,更好的办法是把这些代码放到 DatePickerView 中,而不是放到 ViewController 中,和之前一样,是为了复用和简便。至于如何应用 storyboard 对 view 进行设置这里不再赘述。
Communication(通信)
view controller 中最常产生的就是 通信,包含了和 view 层的通信、和 model 层的通信、和其余 view controller 的通信等。只管这是一个 view controller 必须做的事件,这里仍然有方法可能对代码进行缩减。
对于 view controller 和 view、view controller 和 model 之间的通信曾经存在大量的优良教训,比方应用 KVO 键值模式传值,或者应用 Core Data 中的 NSFetchedResultsController 等。然而,对于 view controller 之间的通信的相干办法却比拟少。
比方在我当初做的我的项目中,一个 view controller 须要依据使用者身份的不同(家长/老师)来对 view controller 的 state 进行不同的设置,而这个 view controller 又在不同的 state 下与不同的 view controller 通信,传递不同的值。在这种状况下 view controller 中的代码会变得相当臃肿和凌乱,所以正确的做法是把这些不同的 state 放到不同的 object 外面,再把它们传给 view controller,让它们依据这个 state 来进行设置和批改,使咱们不再须要被累赘的委托办法搞得非常凌乱。
而在另一种状况下,各个 view controller 之间的跳转逻辑十分复杂,存在着重大的横向依赖,在这种状况下就不合适应用一般的页面跳转模式,而应该应用“中介者模式”,创立一个 coordinator controller,让它来治理页面跳转的逻辑。
论断
事实上当初有各种各样的办法为 view controller 减负,它们无一例外都是向着一个指标后退:写出可保护的代码,只有把这些办法灵活运用,熟记于心,能力真正防止弄出轻便而又难以保护的 view controller。