学习iosblock

1.Block的声明: 返回值类型(^block名称)(参数)

    例如:声明一个无参数无返回值的block:

// 声明:返回值类型(^block变量名)(参数) void(^block)();

2.Block的定义:

    • 方式一:

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

  NSLog(@"调用block1");

};

    • 方式二:block如果没有参数,可以省略()
void(^block2)() = ^{
        
    };
    • 方式三:block定义中,返回值可以省略
int(^block3)() = ^int{//int可以省略
        
        return 2;
    };

  3.Block的作用:保存一段代码,在调用Block的时候执行.

  4.Block的调用:block名(参数);

// 调用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
}
posted @ 2016-04-28 17:08  天灰灰  阅读(144)  评论(0编辑  收藏  举报