Block在iOS开发中使用频率非常高, 现总结一篇block基本用法和大家分享一下!
声明
-
声明格式: 返回值(^block变量名)(参数); 变量名可有可无
void(^block1(int a) void(^block2)(int);
-
声明blocks属性: ARC模式下用strong, MRC模式下用copy
@property(nonatomic ,strong) void(^block)();
定义
定义格式: = ^返回值(参数)
, 可以使用快捷键inline
, 如果没有参数或者返回值, 返回值和参数都可以省略
-
第一种方式
void(^block1)() = ^void(){
NSLog(@"第一种定义方式");
}; -
第二种方式
void(^block2)() = ^()
{
NSLog(@"第二种定义方式");
}; -
第三种定义方式
void(^block3)() = ^
{
NSLog(@"第三种定义方式");
};
作用
和函数功能相似, 保存一段代码.
调用
block的调用是直接去调用的.
block();
应用场景一: 保存代码
-
在Person类中定义一个block属性
@property(nonatomic ,strong) void(^programmer)();
-
在tableViewController类中定义block
Person *person1 = [[Person alloc] init]; person.programmer = ^{ NSLog(@"健身"); }; Person *person2 = [[Person alloc] init]; person.programmer = ^{ NSLog(@"读书"); }; Person *person3 = [[Person alloc] init]; person.programmer = ^{ NSLog(@"撸代码"); }; //添加进persons数组 self.persons = @[person1,person2,person3];
-
在cell数据源方法中调用
Person *person = self.persons[indexPath.row]; if (Person.programmer) { Person.programmer(); }
应用场景二: 传值
传值有两种情况:
顺传
: 上一个控制器给下一个控制器传值. 可以直接在下一个控制器中定义属性来传值.逆传
: 下一个控制器给上一个控制器传值. 可以使用代理, 也可以使用block. 由于block的方便和易用性, 开发中使用block的较多些.
-
在下一个控制器中定义一个block属性: 参数就是下一个控制器需要传给上一个控制器的值
@property (nonatomic, strong) void(^valueBlock)(NSString *);
-
在下一个控制器中定义block, 把值传进去
if (_valueBlock) { //括号里面就是要传的值 _valueBlock(@"这就是要传的值"); }
-
在上一个控制器中调用block, 把参数传进来, value就是下一个控制器传给上一个控制器的值(也就是逆传的值)
nextVC.valueBlock = ^(NSString *value){ NSLog(@"这就是需要传的值%@",value); };
应用场景三: block作为参数
block作为参数, 一般在框架中使用较多.
内部
: 首先定义一个属性, 把block参数保存起来, 在合适的地方调用. 或者是直接在当前方法中调用用.外界
: 定义block, 在block中做一些操作
例如: 封装一个加法类
-
在Add类中定义一个带有block参数的方法
@property (nonatomic, assign) int result; // int(^(int): 参数类型 -(void)add(int(^(int))addBlock;
-
在外部controller类中定义block. 外界可以在block中随意做一些操作, 这些操作的结果都会作为参数传给内部
Add *add = [[Add alloc] init]; [add add:^int(int result){ //在这里面做一些操作 result += 10; return result; }];
-
在Add类中调用block, 可以直接调用. 也可以先保存起来, 再在合适的地方调用
- (void)add:(int (^)(int))addBlock { _result = addBlock(_result); NSLog(@"%i",_result); }
应用场景四: block作为返回值
-
在Add类定义声明一个返回值为block的方法
@property (nonatomic, assign) int result; - (void(^)(int value))add;
-
在外部类中调用block
Add *add = [[Add alloc] init]; add.add(5);
-
在Add类中实现方法
- (void (^)(int value))add { return ^(int value){ _result += value; }; }
Block注意问题
1. 循环引用问题
block会把外界的强指针强引用
默认局部变量都是强指针
__weak typeof(self) weakSelf = self;
typeof(self) strongSelf = weakSelf;
2. 变量引用问题
bock引用变量: 全局变量,静态变量,__block都是指针传递. 局部变量是值传递
释义:
默认block引用外部的局部变量,并且没有任何关键字修饰,都是值传递
如果外部变量用__block或者static修饰,就是指针传递,外面改了,里面也会改
只要是全局变量,Block也是指针传递
3. 内存管理
MRC
block没有访问外部变量,在全局区
如果访问了外部变量,默认block在栈中
MRC中用copy, block才会放在堆中保存.
释义:
- 如果block没有访问外部的局部变量,或者访问的变量被static修饰,或者访问全局变量,那么这个block就是全局的
- 如果block访问外部的局部变量,那么这个block在栈里面的.
- block如果想保存到堆里面,只能使用copy,不能使用retain,使用retain还是在栈里面.
ARC
如果访问了外部变量,默认block在堆中
释义:
- 如果block访问外部的局部变量,那么这个block在堆里面的.
- 在ARC中,block使用strong,放在堆里面保存
- 能使用strong,就不要使用copy.
内存中几个区
全局区:保存全局变量
常量区:保存常量
堆:手动去管理内存,对象
栈:基本变量,int,指针,自动管理
方法区:保存函数