#pragma mark - block的基本使用 只有调用block才会实现block中的代码
传入方声明block调用block,接收方实现block对传过来的值进行使用
#warining 1.声明block 2.调用block 3.实现block
一,Block的声明调用和实现
@implementation RootViewController
- (void)viewDidLoad {
[super viewDidLoad];
//声明一个名字为blockName的block变量
//-(void)funcName:(BOOL)value
void(^blockName)(BOOL value,NSString *str); (1.声明Block这句话blockName是名字,其余的是类型)
blockName = ^(BOOL va1,NSString * str1){ (3.实现block)
NSLog(@"%d,%@",va1,str1);
};
//也可以把这一步和上一步合起来写,就是声明并且实现
void(^blockName)(BOOL value,NSString *str) = ^(BOOL va1,NSString * str1){
NSLog(@"%d,%@",va1,str1);
};
blockName(YES,@"调用block"); (2.调用block)
//例如,声明一个带返回值的block,并且实现 之后调用block
__unsafe_unretained RootViewController *rvc =self;
NSString *(^block2)(UIColor * color) =^(UIColor * colorValue){
rvc.view.backgroundColor = colorValue;
return@"用来返回的字符串";
};
NSString *str = block2([UIColor greenColor]);
NSLog(@"block返回的字符串====%@",str);
//定义block的三种方式,也就是实现block
// block定义:三种方式 = ^(参数){};
// 第一种
void(^block1)() = ^{
NSLog(@"调用了block1");
};
//第二种如果没有参数,参数可以隐藏,如果有参数,定义的时候,必须要写参数,而且必须要有参数变量名
void(^block2)(int) = ^(int a){
};
//第三种 block返回可以省略,不管有没有返回值,都可以省略
int(^block3)() = ^int{
return 3;
};
//所以下面这样写也可以
// int(^block3)() = ^{
// return 3;
// };
//
二,Block在实际开发中的使用
1.把block定义为属性保存代码
@interface ViewController ()
// block怎么声明,就如何定义成属性
@property (nonatomic,strong)void(^block)();
@end
@implementation ViewController
// 1.在一个方法中定义,在另外一个方法调用
// 2.在一个类中定义,在另外一个类中调用
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// void(^)()
void(^block)() = ^{
NSLog(@"调用block");
};
_block = block;
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
// block调用:就去寻找保存代码,直接调用 block有值得时候才待用
if (_block){
_block();
}
}
2.block传值
//传值得根本就是只要拿到对方就能给对方传值,正向传值是这样的,反向传值也是(代理就是拿到他的代理属性,也相当于拿到对方的值)
SecodViewController 里面的代码
@property (nonatomic, copy)void(^block)(NSString *text);
//2.SecodViewController要传递文字,它是需要声明block的一方
//3.SecodViewController它声明,它调用 (这样才能传过去一个数,才叫反向传值)
//4.声明一个属性,指向要调用的block
/*
1.驱动方声明一个block属性 驱动方调用block,执行block中得代码
2.被动方要具体实现block<驱动的block属性应该指向被动方实现的block>
*/
if (_block) {
_block(textField.text);
}
FirstViewController 里面的代码
//下面这代码在第一个控制器里面
//1.实现显示文字的功能,也就是它需要完成block的实现部分
SecodViewController *svc = [[SecodViewController alloc]init];
UILabel *label = (id)[self.view viewWithTag:10];
void(^aBlock)(NSString *text) = ^(NSString *text){
label.text = text;
};
//声明的block指向实现的block
svc.block = aBlock;
3.block变量传递和内存管理
// 如果block引用的是局部变量,Block是值传递 这时候如果是MRC就放在栈里面 ARC就放在堆里面
// 如果block引用的是静态变量,全局变量,__block修饰的变量,都是指针传递 这时候block放在全局区
//block是个对象,苹果说的
MRC的block内存管理:block如果没引用外部局部变量就是放在全局区,如果block引用外部的局部变量就会放在栈里面,因为mrc中没有强指针和弱指针,都是基本数据类型,基本数据类型都是放在栈里面去管理的
- 需要注意防止循环引用,使用__weak关键词修饰
void test5();
int main(int argc,constchar * argv[])
{
test5();
return0;
}
int num = 10;
void test5()
{
void (^block)() = ^{
// block内部能够一直引用全局变量 这时候的block放在全局区
NSLog(@"----num=%d", num);
};
num = 20;
block();
}
void test4()
{
staticint age =10;
void (^block)() = ^{
// block内部能够一直引用被static修饰的变量 因为被static修饰的变量生命周期变了 这时候的block放在全局区
NSLog(@"----age=%d", age);
};
age = 20;
block();
}
void test3()
{
__blockint age =10;
void (^block)() = ^{
// block内部能够一直引用被__block修饰的变量 mrc这时候的block放在栈里面
NSLog(@"----age=%d", age);
};
age = 20;
block();
}
void test2()
{
int age =10; //mrc的这时候的block也是放在栈里面
void (^block)() = ^{
// 普通的局部变量,block内部只会引用它初始的值(block定义那一刻),不能跟踪它的改变
//因为block不知道这个变量什么时候被销毁,所以直接把变量的值放到那个位置,不管以后变量的死活了,但是全局变量,被static修饰的变量,被_block修饰的变量可以一直引用,就是可以跟踪变量的变化
NSLog(@"----age=%d", age);
};
age = 20;
block();
//这里面age的值还是10,不是20
}
void test()
{
int age =10;
void (^block)() = ^{
// ----age=10
NSLog(@"----age=%d", age);
};
block();
}
//block内存管理
/*
block是不是一个对象?是一个对象
如何判断当前文件是MRC,还是ARC
1.dealloc 能否调用super,只有MRC才能调用super
2.能否使用retain,release.如果能用就是MRC
ARC管理原则:只要一个对象没有被强指针修饰就会被销毁,默认局部变量对象都是强指针,存放到堆里面
MRC了解开发常识:1.MRC没有strong,weak,局部变量对象就是相当于基本数据类型
2.MRC给成员属性赋值,一定要使用set方法,不能直接访问下划线成员属性赋值 因为mrc的set方法里面有retain和release操作,不然会造成内存泄漏
//- (void)setName:(NSString *)name
//{
// if (name != _name) {
// [_name release];
// _name = [name retain];
// }
//}
MRC:管理block
总结:只要block没有引用外部局部变量,block放在全局区
只要Block引用外部局部变量,block放在栈里面.
block只能使用copy,不能使用retain,使用retain,block还是在栈里面,这时候我们就没法访问block了,访问栈里面的block会坏内存访问错误
ARC:管理block
只要block引用外部局部变量,block放在堆里面
block使用strong.最好不要使用copy
总结:block如果没引用外部的局部变量就是放在全局区.如果引用了外部的局部变量,如果是MRC就放在栈里面,如果是ARC就放在堆里面 MRC使用copy ARC使用strong,因为用用weak这里的block局部变量就会被销毁,也不能用copy,因为我们只是赋值,没必要做一些copy操作
*/
4.block循环引用
复习modal 和push
当present或则push出来一个控制器,再调用这个控制器的dismiss和pop方法的时候这个控制器就会被销毁,但是dismiss(内部就是把self.presentViewController指针清除)和pop之前控制器不会被销毁
modal出来的控制器不会被销毁时因为这个控制器被self.presentViewController强引用着,所以modal控制器创建代码之后不会立马被销毁
// block造成循环利用:Block会对里面所有强指针变量都强引用一次
//这样防止循环引用,把里面的强指针变量辩称跟弱指针
__weak typeof(self) weakSelf = self;
5.block当做参数
#import <Foundation/Foundation.h>
@interface CacultorManager : NSObject
@property (nonatomic, assign) NSInteger result;
// 计算
- (void)cacultor:(NSInteger(^)(NSInteger result))cacultorBlock;
@end
@implementation CacultorManager
- (void)cacultor:(NSInteger (^)(NSInteger))cacultorBlock
{
if (cacultorBlock) {
_result = cacultorBlock(_result);
}
}
@end
#import "ViewController.h"
#import "CacultorManager.h"
// 怎么区分参数是block,就看有没有^,只要有^.把block当做参数
// 把block当做参数,并不是马上就调用Block,什么时候调用,由方法内部决定
// 什么时候需要把block当做参数去使用:做的事情由外界决定,但是什么时候做由内部决定
/*
需求:封装一个计算器,提供一个计算方法,怎么计算由外界决定,什么时候计算由内部决定.
*/
@interface ViewController ()
@end
@implementation ViewController
- (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);
}
6.block当返回值
#import <Foundation/Foundation.h>
@interface CalculatorManager : NSObject
@property (nonatomic, assign) int result;
//参数是int 返回值是自己
- (CalculatorManager *(^)(int))add;
@end
#import "CalculatorManager.h"
@implementation CalculatorManager
- (CalculatorManager *(^)(int))add
{
//实现block的部分
return ^(int value){
_result += value;
return self;
};
}
#import "ViewController.h"
#import "CalculatorManager.h"
@interface ViewController ()
@end
@implementation ViewController
/*
链式编程思想:把所有的语句用.号连接起来,好处:可读性非常好
*/
/*
需求:封装一个计算器,提供一个加号方法
*/
- (void)viewDidLoad {
[super viewDidLoad];
CalculatorManager *mgr = [[CalculatorManager alloc] init];
// + 5
// mgr.add(5).add(5).add(5).add(5).add(5).add(5)
// mgr.add(5)
// [[[[[mgr add:5] add:5] add:5] add:6] add:7];
mgr.add(5).add(5).add(5).add(5); //返回值都是block,这里是调用block
NSLog(@"%d",mgr.result);
// [mgr add:5];
// [mgr add:5];
// [mgr add:5];
// [mgr add:5];
}
@end