## 瘦身Controller
`MVC`中最大的問題在于`C`層負擔了太多的業務,所以導致`Controller`過大。那么將一些不屬于的`Controller`業務的邏輯分離到其他層中是主要的解決思路。iOS的`MVC`模式也被稱作`重控制器模式`,這是在實際開發中,我們可以看到`V`和`C`難以相互獨立,這兩部分總是緊緊的粘合在一起的:
[](http://sindrilin.com/images/MVC%E6%9E%B6%E6%9E%84%E6%9D%82%E8%B0%88/3.jpeg)
在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](http://casatwy.com/)大神的話:`不應該出現私有方法`。對于控制器來說,私有方法基本都是數據相關的業務處理,將這些業務通過`category`或者策略模式分離出去會讓控制器更加簡潔