学习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
}

浙公网安备 33010602011771号