在一个规范的开发团队中,在代码规范上应该达到每个程序员所写出来的ViewController
结构一致的目标,这样的规范能减小各类delegate
getter
随机出现,ViewController lifecycle
方法处处都是。制定一个规范,可使代码更有利于阅读和维护,固然,规范的制定和团队的架构师的经验而定。
在OS应用架构谈 view层的组织和调用方案这篇文章里提供一个布局规范,以下图所示:html
此外,全部须要初始化的属性能够放在getter
方法中。ios
#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)viewDidLoad { [super viewDidLoad]; self.textLabel = [[UILabel alloc] init]; self.textLabel.textColor = [UIColor blackColor]; self.textLabel ... ... self.textLabel ... ... self.textLabel ... ... [self.view addSubview:self.textLabel]; }
通常状况下,View的建立是应该放在View
中处理。这两种初始化方式相比,很明显第一种比较简洁,将初始化放到getter
中,分工明确,也有利于作测试。数据库
在ios的开发中,MVC的核心就在ViewController
,有些开发人员在不熟悉MVC的状况下,把全部东西通通放入C里面就能够了,因此每每一个ViewController
既有View
的建立,也会有Model
里的业务逻辑。这样一个臃肿的ViewController
很差阅读,很差维护,更不利于作单元测试。
在Lighter View Controllers文章里提出了几个应该属于ViewController
规范化问题的概念。数组
精简 ViewController 的有效方法之一就是实现 UITableViewDataSource 协议相关的代码封装成一个类(好比本文中的 ArraryDataSource )。若是你常常在 UIViewController 中实现 UITableViewDataSource 协议,你会发现相关代码看起来都差很少。缓存
举例说明:网络
# pragma mark Pragma - (Photo*)photoAtIndexPath:(NSIndexPath*)indexPath { return photos[(NSUInteger)indexPath.row]; } - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { return photos.count; } - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { PhotoCell* cell = [tableView dequeueReusableCellWithIdentifier:PhotoCellIdentifier forIndexPath:indexPath]; Photo* photo = [self photoAtIndexPath:indexPath]; cell.label.text = photo.name; return cell; }
能够看到上面方法的实现都与 NSArray 有关,还有一个方法的实现与 Photo 有关(Photo 与 Cell 呈一一对应关系)。下面让咱们来把与 NSArray 相关的代码从 ViewController 中抽离出来,并改用 block 来设置 cell 的视图。架构
@implementation ArrayDataSource - (id)itemAtIndexPath:(NSIndexPath*)indexPath { return items[(NSUInteger)indexPath.row]; } - (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section { return items.count; } - (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(NSIndexPath*)indexPath { id cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath]; id item = [self itemAtIndexPath:indexPath]; configureCellBlock(cell,item); return cell; } @end
如今你能够把 ViewController 中的相关方法移除,而且把 ViewController 的 dataSource 设置为 ArrayDataSource 的实例。布局
void (^configureCell)(PhotoCell*, Photo*) = ^(PhotoCell* cell, Photo* photo) { cell.label.text = photo.name; }; photosArrayDataSource = [[ArrayDataSource alloc] initWithItems:photos cellIdentifier:PhotoCellIdentifier configureCellBlock:configureCell]; self.tableView.dataSource = photosArrayDataSource;
经过上面的方法,你就能够把设置 Cell 视图的工做从 ViewController 中抽离出来。如今你不须要再关心indexPath如何与 NSArrary 中的元素如何关联,当你须要将数组中的元素在其它 UITableView 中展现时你能够重用以上代码。你也能够在 ArrayDataSource 中实现更多的方法,好比tableView:commitEditingStyle:forRowAtIndexPath:
。单元测试
除了以上好处以外,咱们还能够针对这部分实现编写单独的单元测试,并且不再须要处处复制粘贴了。当你使用其余数据容器时,你能够用相似的方式来达到代码复用的效果。
该技巧一样适用于其余 Protocol ,好比 UICollectionViewDataSource 。经过该协议,你能够定义出各类各样的 UICollectionViewCell 。假若有一天,你须要在代码在使用到 UICollectionView 来替代当前的 UITableView,你只须要修改几行 ViewController 中的代码便可完成替换。你甚至可以让你的 DataSource 类同时实现 UICollectionViewDataSource 协议和 UITableViewDataSource 协议。
除此以外,还有不少关于这个方面的讨论:
UITableview代理方法与Viewcontroller分离
不要把 ViewController 变成处理 tableView 的"垃圾桶"
下面的示例代码(另一个工程)位于view controller,做用是找出针对用户active priority的一个列表。
- (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类的一个category中,会让代码更加清晰。此时,在View Controller.m文件中看起来应该是这样的:
- (void)loadPriorities { self.priorities = [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];
}
实际开发中,有一些代码很难将其移至model对象中,可是,很明显这些代码与model是相关的,针对这样的状况,咱们能够单独为其写一个类,例以下面的store类。
本文给出示例工程的初版代码中,有一部分代码是用来从文件中加载数据,并对其进行解析的,这些代码是在view controller中:
- (void)readArchive { NSBundle* bundle = [NSBundle bundleForClass:[self class]]; NSURL *archiveURL = [bundle URLForResource:@"photodata" withExtension:@"bin"]; NSAssert(archiveURL != nil, @"Unable to find archive in bundle."); NSData *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类来作这些事情——经过将这些代码从view controller中剥离出来,不只能够对其重用和单独测试,另外还能对view controller瘦身。Store类专一于数据的加载、缓存,以及对数据库进行配置。这里的Store也常常叫作service layer或者repository。
将网络请求相关逻辑放到Model
层处理
View的建立放到View
层处理
如何给UIViewController瘦身这篇文章很是详细的介绍了臃肿的VC会致使什么问题的出现,同时也提出一个很是清晰的架构图,以下:
只有开发人员在彻底遵照架构规范进行开发时,架构规范的优点才能彻底体现出来。评判一个好的规范的标准很简单,按照规范写出来的代码是否易于阅读、维护、扩展、测试。每每一个成熟的代码规范不是一蹴而就的,须要在实际的开发过程当中,不断的去修改完善。可是最重要的一点,适合本身的才是最好的!