大部分系统、大部分的架构模式,都使用MVC。MVVM、MVP等模式,本质上都是对MVC的演变。ios架构,也是如此。在移动领域,app中v和c都非常容易区分,最重要的是controller这层。往往我们觉得不像v、也不像c的东西,就直接放到controller里面,最直接的结果就是造成controller庞大、复杂,不容易维护,平时迭代升级还好,如果遇到较大的需求变更或者迭代次数变多,就会变得很困难。本人就有非常深的体验。所以要研究app的架构,帮助我们解决问题。
首先,一个架构并不是固定的,但是好的架构总是有那么几点是相同的,不能只抄形,而学不到神。没有绝对好的架构,只有最适合的架构,只有最好的架构师。view层架构是影响工程师迭代周期重要因素之一。加班不是优化迭代周期的正确方式。一般来说,一个不够好的view层架构,主要原因有以下5种:
- 代码混乱不规范
- 过多继承导致的复杂依赖关系
- 模块化程度不够高,组件粒度不够细
- 横向依赖
- 架构设计失去传承
这5个地方是会影响工程师实现需求的效率,进而拖慢迭代周期。当然view架构的其他缺陷会或多或少的产生影响,但在我看来这5个是比较重要的因素。对于第5点要说明一下,架构的设计是一定需要传承的。但实际的情况可能是一个核心的人走了,另一个人顶上,即便任务交接的再完整,都不可避免不同的人有不同的架构思路,从而导致整个架构的流程受到影响。要解决这个问题,要做到2点,首先做架构的时候最好带上一个人,其次是架构尽可能的简单,平缓接手人的学习曲线。当一个大牛从公司立刻的时候,可以说:从我手里出来的代码,终身保修。所以不要想着离职了,就没我什么事了,这不光是职业素养的问题,更是一个人对自己代码是否有足够的自信。传承性对于view层架构非常重要,因为它距离业务最近,改动余地最小。view层跟业务的对接面最广,影响业务层代码的程度也最深,在view架构上牵一发导致业务层动全身面积最大。所以view层架构在所有架构中一旦定型,可修改的空间就最小。我们在考虑view层架构的时候,不光要实现功能,还要考虑更多规范上的东西。制定规范的目的,一方面是防止代码腐蚀view架构,另一方面也是想有所传承。按照规范来,不那么容易出差池。规范也不是一成不变的,大家会提一些意见,什么时候枪毙意见,什么时候采纳意见,就要靠各自的技术和经验了。
view结构代码的规定
严格来讲,制定代码规范不属于view层架构的事情,但它对view层架构未来的影响会很大,所以在设计view架构的时候必须考虑。制定view层规范的重要性在于:
1、提高业务代码的可维护性和可扩展性。
2、防止业务代码对架构产生腐蚀。
3、确保传承。
4、保持架构发展的放心不容易被不合理的意见左右。
代码定义的规范,苹果已经有一套。
建议在规范上再加上下面这一点。
viewcontroller的代码差不多应该是这样:
@property (nonatomic, weak) IBOutlet NSLayoutConstraint *lcCommentsNumWidth; ... #pragma mark - life cycle viewDidLoad viewWillAppear #pragma mark - UITableViewDelegate methods #pragma mark - CustomDelegate methods #pragma mark - event response -(IBAction)onClick:(id)sender #pragma mark - private methods methods #pragma mark - getters and setters - (UIButton *)confirmButton
所有属性都使用getter和setter。
不要在viewDidLoad里初始化你的view然后再add,这样代码就很难看。在viewDidLoad里面只做addSubview的事情,然后在viewWillAppear里面做布局的事情,最后在viewDidAppear里面做Notification的监听之类的事情。至于属性的初始化,则交给getter去做。
比如这样:
#pragma mark - life cycle - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor = [UIColor whiteColor]; [self.view addSubview:self.firstTableView]; [self.view addSubview:self.secondTableView]; [self.view addSubview:self.firstFilterLabel]; [self.view addSubview:self.secondFilterLabel]; [self.view addSubview:self.cleanButton]; [self.view addSubview:self.originImageView]; [self.view addSubview:self.processedImageView]; [self.view addSubview:self.activityIndicator]; [self.view addSubview:self.takeImageButton]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; CGFloat width = (self.view.width - 30) / 2.0f; self.originImageView.size = CGSizeMake(width, width); [self.originImageView topInContainer:70 shouldResize:NO]; [self.originImageView leftInContainer:10 shouldResize:NO]; self.processedImageView.size = CGSizeMake(width, width); [self.processedImageView right:10 FromView:self.originImageView]; [self.processedImageView topEqualToView:self.originImageView]; CGFloat labelWidth = self.view.width - 100; self.firstFilterLabel.size = CGSizeMake(labelWidth, 20); [self.firstFilterLabel leftInContainer:10 shouldResize:NO]; [self.firstFilterLabel top:10 FromView:self.originImageView]; ... ... }这样即便在属性非常多的情况下,还能保持代码整齐,view的初始化都交给getter去做了。总之就是尽量不要出现以下情况:
- (void)viewDidLoad { [super viewDidLoad]; self.textLabel = [[UILabel alloc] init]; self.textLabel.textColor = [UIColor blackColor]; self.textLabel ... ... self.textLabel ... ... self.textLabel ... ... [self.view addSubview:self.textLabel]; }
这种做法就不够干净,都扔到getter里面去好了。
每一个Delegate都把对应的protocol名字带上,delegate方法不要到处乱写,写到一块区域里面去。
比如,UITableViewDelegate的方法集就老老实实写上#pragma mark - UITableViewDelegate。这样有个好处就是,当其他人阅读他并不熟悉的Delegate方法时,能够快速的找到。
event response专门开一个代码区域
所有button、gestureRecognizer的响应事件都放在这个区域里,不要到处乱放。
关于private methods,正常情况下viewcontroller里面不应该有
不是delegate方法,不是event response方法,不是life cycle方法,就是private methods了。对的,正常情况下viewcontroller里面一般是不会存在private methods的,这个private methods一般是用于日期推算、图片裁剪啥的这种小功能。这种小功能要么把它写成一个category,要么把他做成一个模块,哪怕这个模块只有这一个函数也行。
viewcontroller基本上是大部分业务的载体,本身代码已经相当复杂,所以跟业务关联不大的东西能不放viewcontroller就不放。另外,private methods的功能这时候只是你用的到,但是将来说不定其他其他也会用,一开始就独立出来,有利于将来代码的复用。