View controllers 一般是 iOS 項目中最大的文件,而且它們包含了許多沒必要要的代碼。因此 View controllers 中的代碼幾乎老是複用率最低的。接下來我將結合一些我本身看到的東西和平時在使用的方法,來節省ViewController中的代碼量。javascript
歡迎你們關注個人公衆號,我會按期分享一些我在項目中遇到問題的解決辦法和一些iOS實用的技巧,現階段主要是整理出一些基礎的知識記錄下來java
文章也會同步更新到個人博客:
ppsheep.com數組
咱們在平時的編碼中,最常用到的一個控件就是UITableView了,那咱們每次須要使用到tableview的時候,都須要寫一些重複的代碼,好比緩存
#pragma mark - tableview datasource
#pragma mark - tableview datasource
- (PPSFriend *)friendAtIndexPath:(NSIndexPath *)indexPath{
return self.friends[indexPath.row];
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.friends.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
PPSFriendCell *cell = [tableView dequeueReusableCellWithIdentifier:@"friendCell" forIndexPath:indexPath];
PPSFriend *friend = [self friendAtIndexPath:indexPath];
cell.textLabel.text = friend.name;
return cell;
}複製代碼
像上面一些代碼,咱們天天都在寫,每次用到UITableView都須要重寫這些毫無技術可言的代碼,那咱們試想一下可否寫一個封裝類,將這些重複的方法所有封起來,屢次使用呢?答案固然是,能夠的。網絡
上面的代碼,其實都是在圍繞着friends這個數組作一系列的事情,咱們能夠獨立出來一個類,使用一個block或者delegate來設置cell,固然這取決於你的習慣。測試
#pragma mark - tableview config cell
- (void)configCell{
void (^configCell)(PPSFriendCell *, PPSFriend *) = ^(PPSFriendCell *cell, PPSFriend *friend){
cell.textLabel.text = friend.name;
};
PPSArrayDatasource *datasources = [[PPSArrayDatasource alloc] initWithItems:self.friends cellIdentifier:@"friendCell" configureCellBlock:configCell];
self.tableView.dataSource = datasources;
}複製代碼
如今,你能夠把 view controller 中的這 3 個方法去掉了,取而代之,你能夠建立一個 PPSArrayDatasource 類的實例做爲 table view 的 data source。ui
如今你不用擔憂把一個 index path 映射到數組中的位置了,每次你想把這個數組顯示到一個 table view 中時,你均可以複用這些代碼。你也能夠實現一些額外的方法,好比 編碼
tableView:commitEditingStyle:forRowAtIndexPath:複製代碼
還有一種狀況,若是是多個section的狀況下,咱們還能夠再擴展一下,將block定義爲spa
typedef void(^TableViewCellConfigureBlock)(id cell, id item, NSIndexPath *indexPath);複製代碼
#pragma mark - tableview config cell
- (void)configCell{
void (^configCell)(PPSFriendCell *, PPSFriend *, NSIndexPath *) = ^(PPSFriendCell *cell, PPSFriend *friend, NSIndexPath *indexPath){
cell.textLabel.text = friend.name;
};
PPSArrayDatasource *datasources = [[PPSArrayDatasource alloc] initWithItems:self.friends cellIdentifier:@"friendCell" configureCellBlock:configCell];
self.tableView.dataSource = datasources;
}複製代碼
那麼在PPSArrayDatasource的items中裝的應該就是一個一個的數組了,分別對應的每一個section,這裏我只是針對這種狀況說明一下,不論是多個section仍是單個section均可以使用這種方法,來瘦身.net
在 table view controllers 之間共享。
這樣的好處在於,你能夠單獨測試這個類,不再用寫第二遍。該原則一樣適用於數組以外的其餘對象。
下面是在viewcontroller中寫的用來查找一個用戶的目前的優先事項的列表:
- (void)loadPriorities {
NSDate* now = [NSDate date];
NSString* formatString = @"startDate = %@";
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 = %@";
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 保持小巧。Store 對象會關心數據加載、緩存和設置數據棧。它也常常被稱爲服務層或者倉庫。
和上面的主題類似:不要在 view controller 中作網絡請求的邏輯。取而代之,你應該將它們封裝到另外一個類中。這樣,你的 view controller 就能夠在以後經過使用回調(好比一個 completion 的 block)來請求網絡了。這樣的好處是,緩存和錯誤控制也能夠在這個類裏面完成。
不該該在 view controller 中構建複雜的 view 層次結構。你可使用 Interface Builder 或者把 views 封裝到一個 UIView 子類當中。例如,若是你要建立一個選擇日期的控件,把它放到一個名爲 DatePickerView 的類中會比把全部的事情都在 view controller 中作好好得多。再一次,這樣增長了可複用性並保持了簡單。
簡單來講,就是將一個viewcontroller中複雜的view構造,放到一個單獨的view類中,然在viewcontroller中,只須要構建一個這個類就行。
咱們已經看到一些用來建立更小巧的 view controllers 的技術。咱們並非想把這些技術應用到每個可能的角落,只是咱們有一個目標:寫可維護的代碼。知道這些模式後,咱們就更有可能把那些笨重的 view controllers 變得更整潔。