iOS開發中Block的使用


一.Block的基本使用

1.Block的聲明: 返回值類型(^block名稱)(參數)編程

    例如:聲明一個無參數無返回值的block:app

// 聲明:返回值類型(^block變量名)(參數) void(^block)();框架

2.Block的定義:atom

    • 方式一:

void(^block1)() = ^(){ spa

  NSLog(@"調用block1"); 3d

};代理

    • 方式二:block若是沒有參數,能夠省略()
void(^block2)() = ^{
        
    };
    • 方式三:block定義中,返回值能夠省略
int(^block3)() = ^int{//int能夠省略
        
        return 2;
    };

  3.Block的做用:保存一段代碼,在調用Block的時候執行.指針

  4.Block的調用:block名(參數);code

// 調用Block,就會去查看Block所保存代碼
    block1();

  5.Xcode快捷生成block的方式:inlineBlock對象

<#returnType#>(^<#blockName#>)(<#parameterTypes#>) = ^(<#parameters#>) {
        <#statements#>
    };

  6.Block起別名:

// BlockType就是類型別名
typedef void(^BlockType)();

  7.Block定義屬性:

// Block怎麼聲明.就怎麼定義屬性
// block:屬性名
@property (nonatomic ,strong) void(^block)();

二.Block開發使用場景---保存代碼(不經常使用)

  例:新建一個CellItem做爲TableViewController的模型

#import <Foundation/Foundation.h>

@interface CellItem : NSObject

@property (nonatomic ,strong) void(^block)();

@property (nonatomic ,strong) NSString *title;

@end

@implementation CellItem

@end
#import <UIKit/UIKit.h>

@interface TableViewController : UITableViewController

@end

#import "CellItem.h"

// tableView:打電話,發短信,發郵件

@interface TableViewController ()

@property (nonatomic ,strong) NSArray *cellArr;

@end

@implementation TableViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 打電話
    CellItem *item = [[CellItem alloc] init];
    item.title = @"打電話";
    item.block = ^{
        NSLog(@"打電話");
    };
    
    // 發短信
    CellItem *item1 = [[CellItem alloc] init];
    item1.title = @"發短信";
    item1.block = ^{
        NSLog(@"發短信");
    };
    
    // 發郵件
    CellItem *item2 = [[CellItem alloc] init];
    item2.title = @"發郵件";
    item2.block = ^{
        NSLog(@"發郵件");
    };
    
    self.cellArr = @[item,item1,item2];
}

#pragma mark - Table view data source


- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
    
    return self.cellArr.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 = [self.cellArr[indexPath.row] title];
    
    
    return cell;
}

// 點擊cell就會調用
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    CellItem *item = self.cellArr[indexPath.row];
    
    if (item.block) {
        item.block();
    }
}
@end

三.Block開發使用場景---傳值(經常使用)

  數據傳值分順傳和逆傳.

    假設:A控制器push(modal)到B控制器,若A要給B傳值,則此種傳值方式爲順傳;B給A傳值則爲逆傳.

    開發中,你們比較熟悉的方法:順傳--->定義屬性;逆傳--->使用代理.

  下文介紹,使用Block替換代理實現數據逆傳.

  • 首先[使用代理]的狀況: ModalViewController給ViewController傳值
#import <UIKit/UIKit.h>

@class ModalViewController;

@protocol ModalViewControllerDelegate <NSObject>

@optional
// 代理方法:想要告訴代理作什麼事情
- (void)modalViewController:(ModalViewController *)modalVc receiveStr:(NSString *)str;
@end

@interface ModalViewController : UIViewController

@property (nonatomic, weak) id<ModalViewControllerDelegate> delegate;

@end

@implementation ModalViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{

    // 傳值給ViewController
    if ([self.delegate respondsToSelector:@selector(modalViewController:receiveStr:)]) {
        
        [self.delegate modalViewController:self receiveStr:@"123"];
    }
}
@end
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

#import "ModalViewController.h"

@interface ViewController ()<ModalViewControllerDelegate>

@end

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    ModalViewController *modalVc = [[ModalViewController alloc] init];
    
    modalVc.delegate = self;
    
    modalVc.view.backgroundColor = [UIColor yellowColor];
    
    [self presentViewController:modalVc animated:YES completion:nil];
    
}

#pragma mark -ModalViewControllerDelegate
- (void)modalViewController:(ModalViewController *)modalVc receiveStr:(NSString *)str
{
    NSLog(@"接收到值%@",str);
}
@end
  • [使用Block]的狀況:將會在點擊modal控制器的時候打印:接收到123.
#import <UIKit/UIKit.h>

@interface ModalViewController : UIViewController

@property (nonatomic ,strong) void(^valueBlock)(NSString *value);

@end

@implementation ModalViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    
    if (self.valueBlock) {
        self.valueBlock(@"123");
    }
}
@end
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

#import "ModalViewController.h"

@implementation ViewController

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    ModalViewController *modalVc = [[ModalViewController alloc] init];
  
    modalVc.valueBlock = ^(NSString *value){
    
        NSLog(@"接收到%@",value);
        
    };
    
    modalVc.view.backgroundColor = [UIColor yellowColor];
    
    [self presentViewController:modalVc animated:YES completion:nil];
    
}
@end

四.Block內存管理

  首先須要搞清楚一個問題:block是對象嗎?

  蘋果官方文檔告訴你,沒錯,她就是對象.這樣咱們能夠經過%@來打印她.

  1.在非ARC中:

  • block若訪問了[外部局部變量],block存放[]裏面
@interface ViewController ()

@property (nonatomic, copy) void(^block)();

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 0;
    
    void(^block)() = ^{
        
        NSLog(@"調用block");
        
        NSLog(@"%d",a);
    };
    
    self.block = block;
    
    NSLog(@"block - %@",block);//block - <__NSStackBlock__: 0x7fff5b9fa9c8> [棧]
    
    NSLog(@"self.block - %@",self.block);// self.block - <__NSMallocBlock__: 0x7fd2e0418310> [堆]
}
  • block[沒有訪問外部局部變量],則存放到[全局區]
@interface ViewController ()

@property (nonatomic, copy) void(^block)();

@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 0;
    
    void(^block)() = ^{
        
        NSLog(@"調用block");
        
        //NSLog(@"%d",a);
    };
    
    self.block = block;
    
    NSLog(@"block - %@",block); //block - <__NSGlobalBlock__: 0x105d01080>
    
    NSLog(@"self.block - %@",self.block);//self.block - <__NSGlobalBlock__: 0x105d01080>
}
@interface ViewController ()

@property (nonatomic, copy) void(^block)();

@end

static NSString *str = @"iOS";

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 0;
    
    void(^block)() = ^{
        
        NSLog(@"調用block");
        
        //NSLog(@"%d",a);
        
        NSLog(@"%@",str);
    };
    
    self.block = block;
    
    NSLog(@"block - %@",block); //block - <__NSGlobalBlock__: 0x100b8e080>
    
    NSLog(@"self.block - %@",self.block); //block - <__NSGlobalBlock__: 0x100b8e080>
}

 注意: 在非ARC中.不能使用retain引用block,由於block訪問外部局部變量時,retain不會把block放在堆裏面(仍然存放在棧裏),生命週期沒有獲得延長.
     在非ARC中使用copy,纔會把block放在堆裏面.

@interface ViewController ()

//@property (nonatomic, copy) void(^block)();
@property (nonatomic, retain) void(^block)();
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    int a = 0;   
    void(^block)() = ^{
        
        NSLog(@"調用block");
        
        NSLog(@"%d",a);
        
        //NSLog(@"%@",str);
    };
    
    self.block = block;
    
    NSLog(@"block - %@",block);// block - <__NSStackBlock__: 0x7fff55d5b9c8>
    
    NSLog(@"self.block - %@",self.block);//self.block - <__NSStackBlock__: 0x7fff55d5b9c8>
}

  2. ARC環境:

  •  block訪問[外部局部變量],block存放[]裏面

     可使用strong去引用

@interface ViewController ()

@property (nonatomic, strong) void(^block)();
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    
    int a = 0;
    
    // ARC中,默認局部對象變量都是強指針
    
    void(^block)() = ^{
        
        NSLog(@"調用block");
        
        NSLog(@"%d",a);
    };
    
    self.block = block;
    
    NSLog(@"%@",block);//<__NSMallocBlock__: 0x7f9218c3b810>
    NSLog(@"%@",self.block);// <__NSMallocBlock__: 0x7f86fa6133d0>
}

概括:只要block訪問的變量是整個app運行過程當中都存在的變量,那麼block確定存放在全局區.


 五.Block循環引用

   Block循環引用,跟block調用沒有關係
   block只要訪問外部強指針對象變量,就會對這個變量進行強引用.

 1 #import <UIKit/UIKit.h>
 2 
 3 @interface ModalViewController : UIViewController
 4 
 5 @end
 6 
 7 @interface ModalViewController ()
 8 
 9 @property (nonatomic ,strong) void(^block)();
10 
11 @end
12 
13 @implementation ModalViewController
14 
15 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
16 {
17     [self dismissViewControllerAnimated:YES completion:nil];
18 }
19 
20 - (void)dealloc
21 {
22     NSLog(@"%@對象銷燬",self);
23 }
24 
25 - (void)viewDidLoad {
26     [super viewDidLoad];
27     
28     // Block循環引用,跟block調用沒有關係
29     // block只要訪問外部強指針對象變量,就會對這個變量進行強引用.
30     
31     self.block = ^{
32         NSLog(@"延遲打印%@",self);      
33     };
34     
35     self.block();
36 }
37 @end
 1 #import <UIKit/UIKit.h>
 2 
 3 @interface ViewController : UIViewController
 4 
 5 @end
 6 
 7 #import "ModalViewController.h"
 8 
 9 @implementation ViewController
10 
11 - (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
12 {
13     ModalViewController *modalVc = [[ModalViewController alloc] init];
14     
15     modalVc.view.backgroundColor = [UIColor redColor];
16     
17    //self.presentedViewController強引用modalVc
18     [self presentViewController:modalVc animated:YES completion:nil];
19 }
20 @end

  點擊viewController的屏幕modal出來ModalViewController,然而再點擊ModalViewController的屏幕會發現ModalViewController並無被銷燬.

緣由:發生了循環引用.block只要訪問外部強指針對象變量(self==ModalViewController),就會對這個變量進行強引用.ModalViewController對block也進行着強引用.

  解決辦法:使用__weak修飾符.讓block對modalVc進行弱引用.

 

 1 - (void)viewDidLoad {
 2     [super viewDidLoad];
 3     
 4     // Block循環引用,跟block調用沒有關係
 5     // block只要訪問外部強指針對象變量,就會對這個變量進行強引用.
 6     __weak typeof(self) weakSelf = self;
 7     
 8     self.block = ^{
 9         
10     NSLog(@"延遲打印%@",weakSelf);
11 
12     };
13     
14     //self.block();
15 }
16 @end

 

需求:在block中處理延時操做,須要訪問modalVc即便立刻dismiss控制器,保證仍能訪問.即實現dismiss延遲銷燬控制器.

 1 - (void)viewDidLoad {
 2     [super viewDidLoad];
 3     
 4     // Block循環引用,跟block調用沒有關係
 5     // block只要訪問外部強指針對象變量,就會對這個變量進行強引用.
 6     __weak typeof(self) weakSelf = self;
 7     
 8     self.block = ^{
 9         
10         __strong typeof(weakSelf) strongSelf = weakSelf;
11 
12         dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2* NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
13         // 引用的爲內部強指針
14         NSLog(@"延遲打印%@",strongSelf);
15         
16         });
17     
18     };
19     
20     self.block();
21 }

2016-04-10 20:08:19.918 06-Block循環引用[7884:212378] 延遲打印<ModalViewController: 0x7f9a31e1ebe0>
2016-04-10 20:08:19.936 06-Block循環引用[7884:212378] <ModalViewController: 0x7f9a31e1ebe0>對象銷燬

 

self.block();執行完畢而且dismiss,仍然有系統的強指針指向after那個block塊,保證modalVc不被當即銷燬.當延時block快執行完畢,strongSelf指針再也不強引用modalVc,此時modalVc控制器對象才能銷燬.

六.Block變量傳遞

  • 若是[局部變量]被static,__block修飾,那麼都是指針傳遞
  • [全局變量],也是指針傳遞
  • 默認的局部變量,在block中是值傳遞
 1 - (void)viewDidLoad {
 2     [super viewDidLoad];
 3 
 4     // 若是局部變量被static,__block修飾,那麼都是指針傳遞
 5     // 全局變量.也是指針傳遞
 6     __block int a = 5;
 7     
 8     // 默認局部變量 在block中 是 值傳遞
 9     void(^block)() = ^{
10       
11         NSLog(@"%d",a);//10
12     };
13     
14     a = 10;
15     
16     block();
17 }
 1 int a = 5;
 2 
 3 @implementation ViewController
 4 
 5 - (void)viewDidLoad {
 6     [super viewDidLoad];
 7 
 8     // 若是局部變量被static,__block修飾,那麼都是指針傳遞
 9     // 全局變量.也是指針傳遞
10     //__block int a = 5;
11     
12     // 默認局部變量 在block中 是 值傳遞
13     void(^block)() = ^{
14       
15         NSLog(@"%d",a);//10
16     };
17     
18     a = 10;
19     
20     block();
21 }
22 
23 @end
 1 - (void)viewDidLoad {
 2     [super viewDidLoad];
 3 
 4     // 若是局部變量被static,__block修飾,那麼都是指針傳遞
 5     // 全局變量.也是指針傳遞
 6     int a = 5;
 7     
 8     // 默認局部變量 在block中 是 值傳遞
 9     void(^block)() = ^{
10       
11         NSLog(@"%d",a);//5
12     };
13     
14     a = 10;
15     
16     block();
17 }

七.Block開發使用場景---方法參數使用

  其實Block做爲方法的參數使用,咱們開發中常用.

例如:

    [UIView animateWithDuration:1 animations:^{
        
    }];

  只要方法參數帶有^,就表示把block當作參數去使用.
  以上的例子中,把block當作參數去使用,block並非咱們調用,而是系統調用.

  • 那麼,爲何須要把block當作參數去使用呢?

   使用場景: 當本身封裝一個類的時候,有些事情好比怎麼作由外部決定,可是何時去作,由內部決定,這時候採起使用block.
 
需求:封裝一個計算器,提供一個計算方法,怎麼計算由外界去決定,何時計算,本身管理.

 1 #import <Foundation/Foundation.h>
 2 
 3 @interface CalculateManager : NSObject
 4 
 5 /** 保存計算的結果 */
 6 @property (nonatomic, assign) int result;
 7 
 8 /**
 9  *  計算
10  *
11  *  @param block 交待怎麼計算的block
12  */
13 - (void)calculate:(int(^)(int))block;
14 
15 @end
16 
17 @implementation CalculateManager
18 
19 - (void)calculate:(int (^)(int))block
20 {
21      self.result =  block(self.result);
22 }
23 @end
 1 #import <UIKit/UIKit.h>
 2 
 3 @interface ViewController : UIViewController
 4 
 5 @end
 6 
 7 
 8 #import "CalculateManager.h"
 9 
10 @interface ViewController ()
11 
12 @end
13 
14 @implementation ViewController
15 
16 - (void)viewDidLoad {
17     [super viewDidLoad];
18     
19     // 建立計算器管理者
20     CalculateManager *mgr = [[CalculateManager alloc] init];
21    
22     [mgr calculate:^(int result){
23         // 計算
24         result += 5;
25         result *= 2;
26         
27         return result;
28     }];
29     
30     NSLog(@"%d",mgr.result);
31     
32 }
33 @end

八.Block開發使用場景---方法返回值

  其實Block做爲方法的返回值使用,咱們開發中可能不少人都用過.

例如:目前最流行的AutoLayout框架Masonry

    [targetView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.bottom.equalTo(self.view).offset(-20);
        make.leading.equalTo(self.view).offset(20);
        make.size.mas_equalTo(CGSizeMake(100, 100));
    }];

  通常調用方法後面接(),就是把block當作返回值去用.

 

 編程思想:
 鏈式編程:把方法的調用 經過.語法 連接起來,可讀性很是好.

#import <Foundation/Foundation.h>

@interface CalculateManager : NSObject
/** 保存計算的結果 */
@property (nonatomic, assign) int result;

/**
 *  加一個值返回一個計算管理對象
 */
- (CalculateManager *)add:(int)value;

/**
 *  返回一個 帶有整形參數返回值是計算管理對象的 block
 */
- (CalculateManager *(^)(int))add;
@end

@implementation CalculateManager

- (CalculateManager *)add:(int)value
{
    self.result += value;
    
    return self;
}

- (CalculateManager *(^)(int))add
{
    return ^(int value){
        
        self.result += value;
        return self;
    };
}
@end
#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@end

#import "CalculateManager.h"

/*
需求:封裝計算器類,提供加號方法,用於處理加法計算.
 */

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    CalculateManager *mgr = [[CalculateManager alloc] init];
    
    [[[[[mgr add:5] add:5] add:5] add:5] add:5];
    
    mgr.add(5).add(5).add(5).add(5).add(5);
    
    NSLog(@"%d",mgr.result);//50
}
相關文章
相關標籤/搜索