内容:
- block的定义
- block的使用
- block的内存管理
一.block的定义
定义变量和定义属性相同
- 将block想象成变量,如 int 变量,原理相同.
1.定义block属性
/** 定义block属性, sumBlock为参数名 */
@property (nonatomic, copy) int (^sumBlock)(int, int);
2.定义block方法
/** 定义block方法:无参无返回值 */
void (^myBlock)() = ^{
NSLog(@"test");
};
/** 定义block方法:有参有返回值 */
int (^sumBlock)(int, int) = ^(int num1, int num2){
return num1 + num2;
};
int sum = sumBlock(2, 3);
}
3.block作为参数
//定义block参数:参数名称写在外面(如int参数,参数名称也是写在外面)
- (void)test2:(int (^)(int, int))sumBlock{
}
4.block作为返回值
//定义block返回值
- (int (^)(int, int))test3{
return nil;
}
typedef简化定义block
typedef void(^resultBlock)(NSArray<Person *> *arr);
二. block的使用
使用场景一:传值
控制器A modal 出控制器B, B向A传值(逆传)
- 步骤1, B控制器中新建两个属性
/** 逆传 strNi */
@property (nonatomic, strong) NSString *strNi;
/** 逆传 block */
@property (nonatomic, strong) void(^block)(NSString *strNi);
- 步骤2, B控制器中,在适当的时候传值
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
if (_block) {
_block(_strNi);
}
}
- 步骤3, A控制器中, 接收数据
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
FanLuModalController *Bvc = [FanLuModalController alloc] init];
//逆传
Bvc.block = ^(NSString *str){ //str即为B控制器传过来的值
NSLog(@"strNi from Bvc = %@", str);
};
}
使用场景二: 作为参数
- 让他人决定执行的代码
- 自己决定代码执行的时机:或立即执行,或保存起来,等待合适的时机执行.
-- 定义block方法的类
FLCalculatorManager.h
/** calculate result */
@property (nonatomic, assign) int result;
- (void)calculator:(int(^)())block;
FLCalculatorManager.m
- (void)calculator:(int(^)())block{
_result = block();
}
-- 另外一个类:调用这个calculator:方法,传入block
FLCalculatorManager *manager = [[FLCalculatorManager alloc]init];
[manager calculator:^int{
return 5;
}];
使用场景三: 作为返回值,生成链式编程(一般只有在框架中才会使用).如:
make.equalTo(superview).with.insets(padding);
- 链式编程思想:把多个方法用点语法方式连接起来. 优点:可读性非常好.
- 原理解析:
- 点语法就是调用get方法. 每一次点语法, 都是返回一个对象; 接着再用这个对象, 通过点语法调用当前对象的get方法.
注: get方法: 只要是没有参数, 且有返回值的方法,都是get方法.
- 调用get方法 例子:
- (void)viewDidLoad {
[super viewDidLoad];
//调用返回值为block的get的方法:test.
self.test();
}
- (void(^)())test{
return ^{
NSLog(@"平凡而已");
};
}
-
链式编程例子: 通过链式多次相加, 求得最终的result
-
重点:
-
生成一个返回值是block的get方法
-
此block的返回值是当前对象, 有一个参数以参与加法运算.
-
类:FLCalculatorManager.h
/** 最终结果 */
@property (nonatomic, assign) int result;
//返回值是一个block---CalculatorManager *(^)(int),
//这个block的返回值是一个对象---"CalculatorManager *",且这个block需要一个int参数
- (FLCalculatorManager *(^)(int))add;
- 类:FLCalculatorManager.m
- (FLCalculatorManager *(^)(int))add{
//下面代码解析:
//第一个return:返回一个返回值是当前对象的block,是代码块.这个return即是当前方法最终的return
//第二个return:block中的return,返回当前对象
return ^FLCalculatorManager *(int value){
_result += value;
return self;
};
}
- 类控制器调用:
FLCalculatorManager *manager = [[FLCalculatorManager alloc]init];
//调用manager的返回值是block的get方法:add,虽然add方法没有参数,但是其block需要参数 manager.add(10).add(10).add(20);
NSLog(@"result = %d", manager.result); //result最终 == 40
三. block的内存管理
- block:会对 自己代码块中的 外部的 强指针变量 强引用.所以经常使用下句代码, 以防止循环利用:
__weak typeof(self) weakSelf = self;
- 但是如果控制器销毁后, 需要执行的任务还没有执行, 此时我们需要进一步处理:
__strong typeof(weakSelf) strongSelf = weakSelf;
- 下demo是控制器点击时即销毁, 而此时我们还有异步或延迟操作.
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self dismissViewControllerAnimated:YES completion:nil];
}
// block:会对自己代码块中的,外部强指针变量强引用
__weak typeof(self) weakSelf = self;
_block = ^{
// 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();
}
- block访问block之外的变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
__block int a = 3;
// 1.如果a没有__block修饰,且已经传进方法,外面再修改a的值,block内部a的值不会改变.
//因为block会以const的形式将a拷贝到堆区,即block不可以修改外部变量。
// 2.默认block引用外部的局部变量,并且没有任何关键字修饰,都是值传递
// 3.如果外部变量用__block或者static修饰,就是指针传递,外面改了,里面也会改
// 4.只要是全局变量,Block中也是指针传递,可以修改变量
- (void)test{
void(^block)() = ^{
NSLog(@"%d",a);
};
a = 5;
block();
}
MRC与ARC中block的比较
-
在MRC开发中,不能使用下划线成员属性,应该使用get,set方法对属性进行操作.
-
block:strong:ARC, copy:MRC
-
block是对象,所以使用%@打印.
-
MRC:管理block:
-
1.如果block没有访问外部的局部变量,或者访问的变量被static修饰,或者访问全局变量,那么这个block就是全局的.
-
2.如果block访问外部的局部变量,那么这个block在栈中.
-
3.block如果想保存到堆里面,只能使用copy,不能使用retain.因为使用retain系统还是保存block在栈里面.
-
ARC: block保存在堆中.
网友评论