前言:
減負前,控制器是代理,來負責顯示cell。
減負后,Relief類是代理,來負責顯示cell。
優點:減輕了控制器的工作負擔。
例1
控制器減負前:
ViewController.m文件
#import "ViewController.h"
@interface ViewController ()<UITableViewDataSource,UITableViewDelegate>
@property (nonatomic, strong) NSArray *DataSoure;
@end
@implementation ViewController
// 懶加載
-(NSArray *)DataSoure{
if (_DataSoure == nil) {
_DataSoure = [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",@"e",nil];
}
return _DataSoure;
}
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
// 純代碼創建TableView
UITableView *tableView = [[UITableView alloc] init];
// 必須設置frame,否則TableView不顯示
tableView.frame = CGRectMake(80, 100, 200, 300);
// 給TableView一個顯示的載體
[self.view addSubview:tableView];
tableView.dataSource =self;// 核心
tableView.delegate = self;// 核心
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.DataSoure.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *ID = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
cell.textLabel.text = [NSString stringWithFormat:@"第%ld個cell",indexPath.row];
return cell;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NSLog(@"點擊了%ld個cell",indexPath.row);
}
@end
截圖:
UITableView將一個UIViewController設置為它的代理。UITableView在繪制表的時候并不知道要繪制幾個section和幾個row。這個時候他就會向它的代理詢問這些信息。這個時候在controller中的代理方法就會被執行。告訴UITableView去怎樣的繪制。在繪制每個CELL的時候,UITableView也不知道應該怎樣去繪制,這個時候它會去詢問他的代理。代理方法再告訴它去繪制一個怎樣的cell。也就是說代理方法是在View需要一些信息的時候在它的delegate中被執行的,這樣主要是為了MVC的設計結構。
控制器減負后:
ViewController.m文件
#import "ViewController.h"
#import "Relief.h"
@interface ViewController ()
// 在匿名類中利用強指針來保住relief的命 目的:防止relief被釋放,從而無法進入Relief類設置數據并顯示。
@property(nonatomic,strong) Relief *relief;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
// 讓數組中對象的個數成為數據源,所以數組中具體是啥內容并不重要.
NSArray *array = [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",@"e",nil];
// 調用方法,并初始化方法中的block
_relief = [Relief CreateTableViewWithDataSoure:array SelectBlock:^(NSIndexPath *indexPath) {
NSLog(@"點擊了%ld個cell",indexPath.row);
}];
// 純代碼創建TableView
UITableView *tableView = [[UITableView alloc] init];
// 必須設置frame,否則TableView不顯示
tableView.frame = CGRectMake(80, 100, 200, 300);
// 給TableView一個顯示的載體
[self.view addSubview:tableView];
// 讓Relief類的對象成為tableView的數據源和代理,那么底層就會跳轉至Relief類執行代理方法和數據源方法,不會在當前控制器執行,從而把業務分擔了出去,減輕了控制器的負擔。
tableView.dataSource = _relief;// 核心
tableView.delegate = _relief;// 核心
}
@end
Relief.h文件
// 一定要用UIKit框架,不要用Foundation框架,否則會提示UITableView的兩個協議名找不到
#import <UIKit/UIKit.h>
typedef void (^SelectCellBlock)(NSIndexPath *indexPath);
@interface Relief : NSObject<UITableViewDelegate,UITableViewDataSource>
+(instancetype)CreateTableViewWithDataSoure:(NSArray *)DataSoure
SelectBlock:(SelectCellBlock)selectBlock;
@end
Relief.m文件
#import "Relief.h"
@interface Relief()
@property (nonatomic, strong) NSArray *DataSoure;
@property (nonatomic, copy) SelectCellBlock selectBlock;
@end
@implementation Relief
+ (instancetype)CreateTableViewWithDataSoure:(NSArray *)DataSoure
SelectBlock:(SelectCellBlock)select {
// 創建tableView并攜帶數據源和block 唱歌帶著你和我
// CreateTableViewWithDataSourcea
return [[[self class] alloc] initCreateTableViewWithDataSoure:DataSoure
SelectBlock:select];
}
// 方法名必須以init開頭.因為給方法中的self賦值的前提條件是:self必須在init開頭的方法中,否則報錯。
- (instancetype)initCreateTableViewWithDataSoure:(NSArray *)DataSoure SelectBlock:(SelectCellBlock)select {
self = [super init];
if (self) {
self.DataSoure = DataSoure;
self.selectBlock = select;
}
return self;
}
#pragma mark - UITableViewDataSource
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return self.DataSoure.count;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
static NSString *ID = @"cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:ID];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:ID];
}
cell.textLabel.text = [NSString stringWithFormat:@"第%ld個cell",indexPath.row];
return cell;
}
#pragma mark - UITableViewDelegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:NO];
// 點擊cell,就調用block
self.selectBlock(indexPath);
}
@end
截圖:
例2
控制器減負前
Time.h文件
#import <Foundation/Foundation.h>
@protocol AlertViewDelegate<NSObject>
- (void)AlertView:(NSString *)body;
@end
@interface Time : NSObject
@property (nonatomic, weak) id delegate;
- (void) Action;
@end
Time.m文件
#import "Time.h"
@implementation Time
- (void) Action{
[self.delegate AlertView:@"this is title"];//判斷代理有沒有實現代理方法
}
@end
ViewController.m文件
#import "ViewController.h"
#import "Time.h"
@interface ViewController ()<AlertViewDelegate>
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
Time *timer = [[Time alloc] init];
timer.delegate = self;
[timer Action];
}
- (void)AlertView:(NSString *)body
{
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:body message:@"時間到" delegate:self cancelButtonTitle:nil otherButtonTitles:@"確定",nil];
alert.alertViewStyle=UIAlertViewStyleDefault;
[alert show];
}
@end
截圖
控制器減負后
Time.h文件
#import <Foundation/Foundation.h>
@interface Time : NSObject
typedef void (^UIAlertViewBlock)(NSString *body);
@property (nonatomic, copy) UIAlertViewBlock AlertViewBlock;
- (void) Action;
@end
Time.m文件
#import "Time.h"
@implementation Time
- (void) Action
{
if (self.AlertViewBlock)
{
self.AlertViewBlock(@"該起床了");
}
}
@end
ViewController.m文件
#import "ViewController.h"
#import "Time.h"
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
Time *timer = [[Time alloc] init];
//實現block
timer.AlertViewBlock = ^(NSString *body){
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:title message:@"定時到了" delegate:self cancelButtonTitle:nil otherButtonTitles:@"關閉",nil];
alert.alertViewStyle=UIAlertViewStyleDefault;
[alert show];
};
[timer Action];
}
@end
截圖
block和delegate的正確選擇
- 對于block和delegate兩種消息傳遞的方式,沒有孰好孰壞,。不同的情況僅僅只能決定適合用block還是delegate。例如:
- 需要進行多個消息傳遞(即要實現很多接口方法時)應使用delegate。因為代碼更直觀。若用block,代碼看起來費勁,并且不易維護。例如,UIKit框架中的的UITableView中有很多代理方法和數據源方法,如果我們用block來實現的話,block代碼塊中的內容會相當復雜。因為UITableView的每個代理方法和數據源方法都有我們需要返回的數據,這些返回的數據必定寫在block代碼塊中,結果可想而知。
- 一個委托對象的代理屬性只能有一個代理對象,如果想要委托對象調用多個代理對象的回調應該用block。
delegate只是一個保存某個代理對象的地址,如果設置多個代理相當于重新賦值,只有最后一個設置的代理才會被真正賦值。
單例對象最好不要用delegate。單例對象由于始終都只是同一個對象,如果使用delegate,就會造成我們上面說的delegate屬性被重新賦值的問題,最終只能有一個對象可以正常響應代理方法。
代理更加面相過程,block則更面向結果。從設計模式的角度來說,代理更佳面向過程,而block更佳面向結果。例如我們使用NSXMLParserDelegate代理進行XML解析,NSXMLParserDelegate中有很多代理方法,NSXMLParser會不間斷調用這些方法將一些轉換的參數傳遞出來,這就是NSXMLParser解析流程,這些通過代理來展現比較合適。而例如一個網絡請求回來,就通過success、failure代碼塊來展示就比較好。
從性能上來說,block的性能消耗要略大于delegate,因為block會涉及到棧區向堆區拷貝等操作,時間和空間上的消耗都大于代理。而代理只是定義了一個方法列表,在遵守協議對象的objc_protocol_list中添加一個節點,在運行時向遵守協議的對象發送消息即可。