利用block為控制器減負(fù)以及block和delegate的正確選擇

前言:
減負(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

截圖:


31-22.gif

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

截圖:

31-23.gif

例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

截圖

100.95.gif

控制器減負(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

截圖

100.94.gif

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.png
  • 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ā)送消息即可俐镐。

  • 參考鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市哺哼,隨后出現(xiàn)的幾起案子佩抹,更是在濱河造成了極大的恐慌,老刑警劉巖取董,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件棍苹,死亡現(xiàn)場離奇詭異,居然都是意外死亡茵汰,警方通過查閱死者的電腦和手機(jī)廊勃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來经窖,“玉大人坡垫,你說我怎么就攤上這事』拢” “怎么了冰悠?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長配乱。 經(jīng)常有香客問我溉卓,道長,這世上最難降的妖魔是什么搬泥? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任桑寨,我火速辦了婚禮,結(jié)果婚禮上忿檩,老公的妹妹穿的比我還像新娘尉尾。我一直安慰自己,他們只是感情好燥透,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布沙咏。 她就那樣靜靜地躺著,像睡著了一般班套。 火紅的嫁衣襯著肌膚如雪肢藐。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天吱韭,我揣著相機(jī)與錄音吆豹,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛痘煤,可吹牛的內(nèi)容都是我干的鸳吸。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼速勇,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了坎拐?” 一聲冷哼從身側(cè)響起烦磁,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哼勇,沒想到半個月后都伪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡积担,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年陨晶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片帝璧。...
    茶點(diǎn)故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡先誉,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出的烁,到底是詐尸還是另有隱情褐耳,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布渴庆,位于F島的核電站铃芦,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏襟雷。R本人自食惡果不足惜刃滓,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望耸弄。 院中可真熱鬧咧虎,春花似錦、人聲如沸计呈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽震叮。三九已至胧砰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間苇瓣,已是汗流浹背尉间。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人哲嘲。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓贪薪,卻偏偏與公主長得像,于是被迫代替她去往敵國和親眠副。 傳聞我的和親對象是個殘疾皇子画切,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容

  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,160評論 30 470
  • 設(shè)計(jì)模式是什么? 你知道哪些設(shè)計(jì)模式囱怕,并簡要敘述霍弹? 設(shè)計(jì)模式是一種編碼經(jīng)驗(yàn),就是用比較成熟的邏輯去處理某一種類型的...
    不懂后悔閱讀 826評論 0 53
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫娃弓、插件典格、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,119評論 4 61
  • 當(dāng)你選定一條路, 另一條路的風(fēng)景便與你無關(guān)台丛。?
    袁益君閱讀 163評論 0 1
  • 減肥減得太猛耍缴,身體亂套,大姨媽來了二十多天挽霉。去醫(yī)院查防嗡。 結(jié)婚沒? 沒有侠坎。 有男朋友嗎本鸣? 有。 有性生活嗎硅蹦? 有荣德。 ...
    撕裂的光線閱讀 186評論 0 0