前言:
減負(fù)前,控制器是代理,來負(fù)責(zé)顯示cell唐片。
減負(fù)后,Relief類是代理,來負(fù)責(zé)顯示cell徒仓。
優(yōu)點(diǎn):減輕了控制器的工作負(fù)擔(dān)璃岳。
例1
控制器減負(fù)前:
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];
// 純代碼創(chuàng)建TableView
UITableView *tableView = [[UITableView alloc] init];
// 必須設(shè)置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(@"點(diǎn)擊了%ld個cell",indexPath.row);
}
@end
截圖:
UITableView將一個UIViewController設(shè)置為它的代理涡尘。UITableView在繪制表的時(shí)候并不知道要繪制幾個section和幾個row。這個時(shí)候他就會向它的代理詢問這些信息歼争。這個時(shí)候在controller中的代理方法就會被執(zhí)行拜马。告訴UITableView去怎樣的繪制。在繪制每個CELL的時(shí)候沐绒,UITableView也不知道應(yīng)該怎樣去繪制俩莽,這個時(shí)候它會去詢問他的代理。代理方法再告訴它去繪制一個怎樣的cell洒沦。也就是說代理方法是在View需要一些信息的時(shí)候在它的delegate中被執(zhí)行的,這樣主要是為了MVC的設(shè)計(jì)結(jié)構(gòu)豹绪。
控制器減負(fù)后:
ViewController.m文件
#import "ViewController.h"
#import "Relief.h"
@interface ViewController ()
// 在匿名類中利用強(qiáng)指針來保住relief的命 目的:防止relief被釋放,從而無法進(jìn)入Relief類設(shè)置數(shù)據(jù)并顯示价淌。
@property(nonatomic,strong) Relief *relief;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor redColor];
// 讓數(shù)組中對象的個數(shù)成為數(shù)據(jù)源,所以數(shù)組中具體是啥內(nèi)容并不重要.
NSArray *array = [NSArray arrayWithObjects:@"a",@"b",@"c",@"d",@"e",nil];
// 調(diào)用方法,并初始化方法中的block
_relief = [Relief CreateTableViewWithDataSoure:array SelectBlock:^(NSIndexPath *indexPath) {
NSLog(@"點(diǎn)擊了%ld個cell",indexPath.row);
}];
// 純代碼創(chuàng)建TableView
UITableView *tableView = [[UITableView alloc] init];
// 必須設(shè)置frame,否則TableView不顯示
tableView.frame = CGRectMake(80, 100, 200, 300);
// 給TableView一個顯示的載體
[self.view addSubview:tableView];
// 讓Relief類的對象成為tableView的數(shù)據(jù)源和代理,那么底層就會跳轉(zhuǎn)至Relief類執(zhí)行代理方法和數(shù)據(jù)源方法申眼,不會在當(dāng)前控制器執(zhí)行瞒津,從而把業(yè)務(wù)分擔(dān)了出去,減輕了控制器的負(fù)擔(dān)括尸。
tableView.dataSource = _relief;// 核心
tableView.delegate = _relief;// 核心
}
@end
Relief.h文件
// 一定要用UIKit框架巷蚪,不要用Foundation框架,否則會提示UITableView的兩個協(xié)議名找不到
#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 {
// 創(chuàng)建tableView并攜帶數(shù)據(jù)源和block 唱歌帶著你和我
// CreateTableViewWithDataSourcea
return [[[self class] alloc] initCreateTableViewWithDataSoure:DataSoure
SelectBlock:select];
}
// 方法名必須以init開頭.因?yàn)榻o方法中的self賦值的前提條件是:self必須在init開頭的方法中,否則報(bào)錯。
- (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];
// 點(diǎn)擊cell,就調(diào)用block
self.selectBlock(indexPath);
}
@end
截圖:
例2
控制器減負(fù)前
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"];//判斷代理有沒有實(shí)現(xiàn)代理方法
}
@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:@"時(shí)間到" delegate:self cancelButtonTitle:nil otherButtonTitles:@"確定",nil];
alert.alertViewStyle=UIAlertViewStyleDefault;
[alert show];
}
@end
截圖
控制器減負(fù)后
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];
//實(shí)現(xiàn)block
timer.AlertViewBlock = ^(NSString *body){
UIAlertView *alert=[[UIAlertView alloc] initWithTitle:title message:@"定時(shí)到了" delegate:self cancelButtonTitle:nil otherButtonTitles:@"關(guān)閉",nil];
alert.alertViewStyle=UIAlertViewStyleDefault;
[alert show];
};
[timer Action];
}
@end
截圖
block和delegate的正確選擇
- 對于block和delegate兩種消息傳遞的方式,沒有孰好孰壞,濒翻。不同的情況僅僅只能決定適合用block還是delegate屁柏。例如:
- 需要進(jìn)行多個消息傳遞(即要實(shí)現(xiàn)很多接口方法時(shí))應(yīng)使用delegate。因?yàn)榇a更直觀有送。若用block,代碼看起來費(fèi)勁,并且不易維護(hù)淌喻。例如,UIKit框架中的的UITableView中有很多代理方法和數(shù)據(jù)源方法,如果我們用block來實(shí)現(xiàn)的話,block代碼塊中的內(nèi)容會相當(dāng)復(fù)雜雀摘。因?yàn)閁ITableView的每個代理方法和數(shù)據(jù)源方法都有我們需要返回的數(shù)據(jù)裸删,這些返回的數(shù)據(jù)必定寫在block代碼塊中,結(jié)果可想而知。
- 一個委托對象的代理屬性只能有一個代理對象阵赠,如果想要委托對象調(diào)用多個代理對象的回調(diào)應(yīng)該用block涯塔。
delegate只是一個保存某個代理對象的地址,如果設(shè)置多個代理相當(dāng)于重新賦值清蚀,只有最后一個設(shè)置的代理才會被真正賦值匕荸。
單例對象最好不要用delegate。單例對象由于始終都只是同一個對象枷邪,如果使用delegate榛搔,就會造成我們上面說的delegate屬性被重新賦值的問題,最終只能有一個對象可以正常響應(yīng)代理方法齿风。
代理更加面相過程药薯,block則更面向結(jié)果。從設(shè)計(jì)模式的角度來說救斑,代理更佳面向過程童本,而block更佳面向結(jié)果。例如我們使用NSXMLParserDelegate代理進(jìn)行XML解析脸候,NSXMLParserDelegate中有很多代理方法穷娱,NSXMLParser會不間斷調(diào)用這些方法將一些轉(zhuǎn)換的參數(shù)傳遞出來,這就是NSXMLParser解析流程运沦,這些通過代理來展現(xiàn)比較合適泵额。而例如一個網(wǎng)絡(luò)請求回來,就通過success携添、failure代碼塊來展示就比較好嫁盲。
從性能上來說,block的性能消耗要略大于delegate烈掠,因?yàn)閎lock會涉及到棧區(qū)向堆區(qū)拷貝等操作羞秤,時(shí)間和空間上的消耗都大于代理缸托。而代理只是定義了一個方法列表,在遵守協(xié)議對象的objc_protocol_list中添加一個節(jié)點(diǎn)瘾蛋,在運(yùn)行時(shí)向遵守協(xié)議的對象發(fā)送消息即可俐镐。