6.瘦身Controller

MVC中最大的问题在于C层负担了太多的业务,所以导致Controller过大。那么将一些不属于的Controller业务的逻辑分离到其他层中是主要的解决思路。iOS的MVC模式也被称作重控制器模式,这是在实际开发中,我们可以看到VC难以相互独立,这两部分总是紧紧的粘合在一起的:

在iOS中,Controller管理着自己的视图的生命周期,因此会和这个视图本身产生较大的耦合关系。这种耦合最大的表现在于我们的V总是几乎在C中创建的,生命周期由C层来负责,所以对于下面这种视图创建代码我们并不会觉得有什么问题:

//ViewController.m
- (void)viewDidLoad {
    [super viewDidLoad];
    UIButton *btn = [[UIButton alloc] initWithFrame: CGRectMake(20, 60, self.view.bounds.size.width - 40, 45)];
    [btn setTitle: @"点击" forState: UIControlStateNormal];
    [btn addTarget: self action: @selector(clickButton:) forControlEvents: UIControlEventTouchUpInside];
    [self.view addSubview: btn];
}

但是按照业务逻辑来说,我们可以在Controller里面创建视图,但是配置的任务不应该轻易的放在C层。因此,这些创建工作完全可以使用视图的category来实现配置业务,对于常用的控件你都可以尝试封装一套构造器来减少Controller中的代码:

@interface UIButton(LXDDesignedInitializer)

+ (instancetype)buttonWithFrame: (CGRect)frame text: (NSString *)text;
+ (instancetype)buttonWithFrame: (CGRect)frame text: (NSString *)text textColor: (UIColor *)textColor;
+ (instancetype)buttonWithFrame: (CGRect)frame text: (NSString *)text textColor: (UIColor *)textColor fontSize: (CGFloat)fontSize target: (id)target action: (SEL)action;
+ (instancetype)buttonWithFrame: (CGRect)frame text: (NSString *)text textColor: (UIColor *)textColor fontSize: (CGFloat)fontSize cornerRadius: (CGFloat)cornerRadius;
+ (instancetype)buttonWithFrame: (CGRect)frame text: (NSString *)text textColor: (UIColor *)textColor fontSize: (CGFloat)fontSize cornerRadius: (CGFloat)cornerRadius target: (id)target action: (SEL)action backgroundColor: (UIColor *)backgroundColor;
+ (instancetype)buttonWithFrame:(CGRect)frame text:(NSString *)text textColor:(UIColor *)textColor fontSize: (CGFloat)fontSize cornerRadius: (CGFloat)cornerRadius target: (id)target action: (SEL)action image: (NSString *)image selectedImage: (NSString *)selectedImage backgroundColor: (UIColor *)backgroundColor;

@end

此外,如果我们需要使用代码设置视图的约束时,Masonry大概是减少这些代码的最优选择。视图配置代码是我们瘦身Controller的一部分,其次在于大量的代理协议方法。因此,使用category将代理方法实现移到另外的文件中是一个好方法:

@interface ViewController (LXDDelegateExtension)<UITableViewDelegate, UITableViewDataSource>

@end

@implementation ViewController(LXDDelegateExtension)

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    //configurate and return cell
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    //return rows in section of cell number
}

@end

这种方式简单的把代理方法挪移到category当中,但是也存在着一些缺点,因此适用场合会比较局限:

  • category中不能访问原类的私有属性、方法。这点Swift要超出OC太多

  • 在减少原类的代码量的情况下实际上使得整个项目结构读起来更加复杂

笔者在通过上述的方式分离代码之后,控制器层的代码量基本可以得到控制。当然,除了上面提到的之外,还有一个小的补充,我们基本都使用#pragma mark给控制器的代码分段,一个比较有层次的分段注释大概是这样的:

#pragma mark - View Life
//视图生命周期
#pragma mark - Setup
//创建视图等
#pragma mark - Lazy Load、Getter、Setter
//懒加载、Getter和Setter
#pragma mark - Event、Callbacks
//事件、回调等
#pragma mark - Delegate And DataSource
//代理和数据源方法
#pragma mark - Private
//私有方法

认真看是不是发现了其实很多的业务逻辑我们都能通过category的方式从Controller中分离出去。在这里我非常同意Casa大神的话:不应该出现私有方法。对于控制器来说,私有方法基本都是数据相关的业务处理,将这些业务通过category或者策略模式分离出去会让控制器更加简洁

文章导航