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

前言:
減負前,控制器是代理,來負責顯示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

截圖:


31-22.gif

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

截圖:

31-23.gif

例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

截圖

100.95.gif

控制器減負后

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

截圖

100.94.gif

block和delegate的正確選擇

  • 對于block和delegate兩種消息傳遞的方式,沒有孰好孰壞,。不同的情況僅僅只能決定適合用block還是delegate。例如:
  • 需要進行多個消息傳遞(即要實現很多接口方法時)應使用delegate。因為代碼更直觀。若用block,代碼看起來費勁,并且不易維護。例如,UIKit框架中的的UITableView中有很多代理方法和數據源方法,如果我們用block來實現的話,block代碼塊中的內容會相當復雜。因為UITableView的每個代理方法和數據源方法都有我們需要返回的數據,這些返回的數據必定寫在block代碼塊中,結果可想而知。
  • 一個委托對象的代理屬性只能有一個代理對象,如果想要委托對象調用多個代理對象的回調應該用block。
delegate.png
  • delegate只是一個保存某個代理對象的地址,如果設置多個代理相當于重新賦值,只有最后一個設置的代理才會被真正賦值。

  • 單例對象最好不要用delegate。單例對象由于始終都只是同一個對象,如果使用delegate,就會造成我們上面說的delegate屬性被重新賦值的問題,最終只能有一個對象可以正常響應代理方法。

  • 代理更加面相過程,block則更面向結果。從設計模式的角度來說,代理更佳面向過程,而block更佳面向結果。例如我們使用NSXMLParserDelegate代理進行XML解析,NSXMLParserDelegate中有很多代理方法,NSXMLParser會不間斷調用這些方法將一些轉換的參數傳遞出來,這就是NSXMLParser解析流程,這些通過代理來展現比較合適。而例如一個網絡請求回來,就通過success、failure代碼塊來展示就比較好。

  • 從性能上來說,block的性能消耗要略大于delegate,因為block會涉及到棧區向堆區拷貝等操作,時間和空間上的消耗都大于代理。而代理只是定義了一個方法列表,在遵守協議對象的objc_protocol_list中添加一個節點,在運行時向遵守協議的對象發送消息即可。

  • 參考鏈接

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結起來就是把...
    Dove_iOS閱讀 27,217評論 30 472
  • 設計模式是什么? 你知道哪些設計模式,并簡要敘述? 設計模式是一種編碼經驗,就是用比較成熟的邏輯去處理某一種類型的...
    不懂后悔閱讀 839評論 0 53
  • 發現 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 12,252評論 4 61
  • 當你選定一條路, 另一條路的風景便與你無關。?
    袁益君閱讀 168評論 0 1
  • 減肥減得太猛,身體亂套,大姨媽來了二十多天。去醫院查。 結婚沒? 沒有。 有男朋友嗎? 有。 有性生活嗎? 有。 ...
    撕裂的光線閱讀 258評論 0 0