Block基本使用
-
1.1、block声明:
返回值(^block
变量名)(参数)void(^block)();
-
1.2、block定义:三种方式 = ^(参数){};
-
第一种
void(^block1)() = ^{
NSLog(@"调用了block1");
};
- 第二种 如果没有参数,参数可以隐藏,如果有参数,定义的时候,必须要写参数,而且必须要有参数变量名
void(^block2)(int) = ^(int a){
};
- 第三种 block返回可以省略,不管有没有返回值,都可以省略
int(^block3)() = ^int{
return 3;
};
block类型:int(^)(NSString *)
int(^block4)(NSString *) = ^(NSString *name){
return 2;
};
1.3、block调用
block1();
1.4、block快捷方式 inline
block开发使用场景(保存代码)
-
2.1、block如何定义成属性
- 2.1.1、block怎么声明,就如何定义成属性
@property (nonatomic, strong) void(^block)();
- 2.1.2、BlockType:类型别名
typedef void(^BlockType)();
@property (nonatomic, strong) BlockType block1;
- 2.1.1、block怎么声明,就如何定义成属性
-
block开发使用场景(传值)
Block内存管理
- 4.1、面试题:
block是不是一个对象?是一个对象 - 4.2、如何判断当前文件是MRC,还是ARC
- 1.dealloc 能否调用super,只有MRC才能调用super
- 2.能否使用retain,release.如果能用就是MRC
- 4.3、block的内存管理分两种情况:MRC和ARC
- 4.3.1、MRC了解开发常识:
1.MRC没有strong,weak,局部变量对象就是相当于基本数据类型(基本数据类型放在栈里,代码块结束就销毁)
2.MRC给成员属性赋值,一定要使用set方法,不能直接访问下划线成员属性赋值
解释:在set方法里会做些额外的处理,如果用_name就不会有relase和retain操作,如:
- 4.3.1、MRC了解开发常识:
@property (nonatomic, retain) NSString *name;
- (void)setName:(NSString *)name
{
if (name != _name) {
[_name release];
_name = [name retain];
}
}
```
- 4.3.2、MRC:管理block
- 1、只要Block引用外部局部变量,block放在栈里面.
- 2、block只能使用copy,不能使用retain,使用retain,block还是在栈里面(代码块结束就销毁),而使用copy,block就在堆里面
解释:
> ```
1、
__block int a = 3;
void(^block)() = ^{
NSLog(@"调用block%d",a);
};
此时block引用外部的局部变量,在栈里
2、
@property (nonatomic, copy) void(^block)();
- MRC下:
- (void)viewDidLoad {
[super viewDidLoad];
__block int a = 3;
void(^block)() = ^{
NSLog(@"调用block%d",a);
};
self.block = block;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.block();
}
- 4.3.3、ARC:管理block
ARC管理原则:
只要一个对象没有被强指针修饰就会被销毁,默认局部变量对象都是强指针,存放到堆里面
ARC:管理block
只要block引用外部局部变量,block放在堆里, block使用strong.最好不要使用copy
- ARC下:
@property (nonatomic, strong) void(^block)();
//@property (nonatomic, copy) void(^block)();
- (void)viewDidLoad {
[super viewDidLoad];
// self.name = @"123";
int a = 3;
void(^block)() = ^{
NSLog(@"%d",a);
};
_block = block;
NSLog(@"%@",_block);
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
_block();
}
思考?
- 1.为什么block在堆里,也不能用weak来修饰?
- 答:ARC下,默认局部变量对象都是强指针,block指针强引用block对象,代码块结束就会释放,所以需要对block对象再进行一次强引用。为什么要对局部变量进行一次强引用,就是为了防止代码块销毁
- 2.为什么用strong不用copy
- 答:
@property (nonatomic, strong) NSString *name;
现在这个视频讲的是对字符串不必用copy,因为不可变给不可变赋值,不用分配内存
- (void)setName:(NSString *)name
{
_name = [name copy];
}
这个代码是copy要进行的操作,会在内部判断是否需要分布新的内存,如可变给不可变赋值需要分布内存,若不可变给不可变赋值就没必要分布内存
- (void)setName:(NSString *)name
{
_name = name;
}
这个代码是strong要进行的操作,直接赋值
总结:
只要block没有引用外部局部变量,block放在全局区
void(^block)() = ^{
NSLog(@"调用block%d");
};
static int a = 3;(这个不是局部变量,这个static更改了a的生命周期)
void(^block)() = ^{
NSLog(@"调用block%d",a);
};
//此时block没有引用外部的局部变量,在全局区里
Block循环引用
@property (nonatomic, strong) void(^block)();
- (void)viewDidLoad {
[super viewDidLoad];
// block造成循环利用:Block会对里面所有强指针变量都强引用一次
_block = ^{
NSLog(@"%@",self);
};
_block();
}
循环引用图:

解析图:
图:self里有block属性,只想block变量,而block变量里有self,会对里面的强指针变量强引用一次,self就会强指向该对象,就会造成相互引用。
解决方法:
@property (nonatomic, strong) void(^block)();
- (void)viewDidLoad {
[super viewDidLoad];
// block造成循环利用:Block会对里面所有强指针变量(所有的外部的对象变量)都强引用一次
__weak typeof(self) weakSelf = self;
_block = ^{
NSLog(@"%@",weakSelf);
};
_block();
}
指针图:

指针图解析:把self包装成弱指针,block就不会再强引用了
深入block循环利用:
- 问题:若block里有延时操作,可能就已经销毁,需要再强引用来防止对象的销毁
@property (nonatomic, strong) void(^block)();
- (void)dealloc
{
NSLog(@"ModalViewController销毁");
}
- (void)viewDidLoad {
[super viewDidLoad];
// block造成循环利用:Block会对里面所有强指针变量都强引用一次
__weak typeof(self) weakSelf = self;
_block = ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf);
});
};
_block();
}
指针图:

解析图:
strongSelf
指针对本对象强引用,__strong typeof(weakSelf) strongSelf
相当于局部变量,after block
中就会用strongSelf
指针对本对象强引用,最外层block
执行完毕后,strongSelf
指针会销毁、2s后after block
会销毁之后本对象才会被销毁
Block变量传递
- 如果是局部变量,Block是值传递(外面修改不影响里面)
- (void)viewDidLoad {
[super viewDidLoad];
int a = 3;
void(^block)() = ^{
NSLog(@"%d",a); //a为3
};
a = 5;
block();
}
- 如果是静态变量,全局变量,__block修饰的变量,block都是指针传递(外面的修改影响block里面的值)
- (void)viewDidLoad {
[super viewDidLoad];
__block int a = 3;
void(^block)() = ^{
NSLog(@"%d",a); //值为5
};
a = 5;
block();
}
Block开发使用场景(参数使用)
- 把block当做参数,并不是马上就调用Block,什么时候调用,由方法内部决定
- 什么时候需要把block当做参数去使用:做的事情由外界(非本类)决定,但是什么时候做由内部决定.
需求:封装一个计算器,提供一个计算方法,怎么计算由外界决定,什么时候计算由内部决定.
- (void)viewDidLoad {
[super viewDidLoad];
// 创建计算器管理者
CacultorManager *mgr = [[CacultorManager alloc] init];
[mgr cacultor:^(NSInteger result){
result += 5;
result += 6;
result *= 2;
return result;
}];
NSLog(@"%ld",mgr.result);
}
@implementation CacultorManager
- (void)cacultor:(NSInteger (^)(NSInteger))cacultorBlock {
if (cacultorBlock) {
_result = cacultorBlock(_result);
}
}
block开发中使用场景(返回值)
- 体现了一种思想:
- 链式编程思想:把所有的语句用.号连接起来,好处:可读性非常好
如: messay里的make.center.equalTo(ws.view);
解析:make.center.equalTo得到一个block,make.center.equalTo(ws.view)去调用block
- self.test();
- 解析:self.test();发两步:1、self.test是调用get方法,返回一个block,2、self.test()调用block
- (void(^)())test
{
// void(^block)() = ^{
//
// };
return ^{
NSLog(@"调用了block");
};
}
需求:封装一个计算器,提供一个加号方法
- (void)viewDidLoad {
[super viewDidLoad];
CalculatorManager *mgr = [[CalculatorManager alloc] init];
// mgr.add(5).add(5).add(5).add(5).add(5).add(5)
// mgr.add(5)解析:mgr.add调用get方法返回一个block,mgr.add(5)调用的block是参数为5,mgr.add(5).add(5)需要返回mgr对象才可以一直.add调用下去
mgr.add(5).add(5).add(5).add(5);
}
@implementation CalculatorManager
- (CalculatorManager *(^)(int))add
{
return ^(int value){
_result += value;
return self;
};
}
网友评论