一、什么是block?
按照苹果官方文档的说法,block是一个oc对象。(原文:Blocks are Objective-C objects, which means they can be added to collections like NSArray or NSDictionary. )其实,本质就是一个函数。
当然,block也可以理解成oc的一种数据类型,我个人认为把block理解成一种数据类型更便于我们理解和记忆。下面我就从这个角度详细介绍block。
二、block的介绍
我们在定义一个变量的时候,都包含了以下三个步骤:
1、确定这个变量的数据类型
2、声明变量
3、定义变量
// 1.比如确定这个数据是 int 类型
int value ; // 2.声明
value = 15 ; // 3.定义
// 当然,也可以声明的同时进行定义
int value = 15 ;
从代码中认识block
例如:
// 1.一个有参数有返回值的block类型:int (^) (int ,int)
int (^block1) (int ,int); // 2.block的声明
block1 = ^int(int n1,int n2){ // 3.block的定义
int sum = n1 + n2;
return sum;
}
block1(10,20); // 4.block的调用
// 当然,也可以声明的同时进行定义
void(^block2)(int) = ^(int a){
NSLog(@"你输入的值是:%d",a );
};
block2(999);
// 我们可以看出block2的类型是:void(^)(int)
1.block的类型
格式:返回值类型(^)(block参数类型)
block的类型大致可以分为四种:
a: 无参数,无返回值
b: 无参数,有返回值
c: 有参数,无返回值
d: 有参数,有返回值
2.block的声明
格式:**返回值类型(^Block变量名)(block参数类型 参数变量名) **
int (^block11)(int a,int b);
int (^block22)(int,int); // 注意:参数变量名可以省略
3.block的定义
格式:^返回值类型(参数类型 参数变量名) { // 代码块 } ;
// 注意1:声明的时候参数的变量名可以省略,当定义是参数的变量名一定不能省略
void(^block111)(int) = ^void(int a) {
};
int(^block222)(int) = ^int(int a) {
return 2;
};
// 注意2:返回值的类型是可以省略的
void(^block111)(int a) = ^(int a) {
};
int(^block222)(int a) = ^(int a) {
return 2;
};
// 注意3: 当没有返回值,没有参数时,可以省略括号
void(^block333)() = ^{
};
4.block的调用
格式:block变量名(参数);
5.如何声明一个block属性
方式一:
@interface ViewController ()
@property (nonatomic, strong) void(^block)(); // 方式一
@end
方式二:
typedef void(^BlockType)(); // BlockType:block类型别名
@interface ViewController ()
@property (nonatomic, assign) BlockType block1; // 方式二
@end
三、block的内存管理
我们都知道内存是可以划分成堆,栈,方法区,常量区,全局区5个区。而block是可能被存放在以下三个区中的任意一个中。
1.在全局静态区:_NSConcreteGlobalBlock
2.在栈中:_NSConcreteStackBlock
3.在堆中:_NSConcreteMallocBlock
MRC中:
1.如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
2.如果block访问外部的局部变量,block存放在"栈"里面
MRC:不能使用retain声明block,依然放在栈里面,会自动销毁.
MRC:使用copy声明block,才会放在堆里面.
ARC如何管理Block:
1.如果block没有访问外部的局部变量或者局部变量被static修饰,block默认存放在"全局区"
2.如果block访问外部的局部变量,block存放在"堆"里面
ARC:使用strong声明block,不要使用weak.
四、block的循环引用问题
------------“你强我就强,你弱我就弱”------------
在block的内部访问了一个对象:
1.如果这个对象本来就有强指针指着,那么这个block也会强引用这个对象;
2.如果这个对象只有弱指针引着,那么这个block就不会对这个对象进行强引用。
Person类:
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property(nonatomic,strong)NSString *name; // 姓名
@property(nonatomic,assign)int age; // 年龄
@property(nonatomic,assign)int height; // 体重
// 声明block
@property(nonatomic,strong) void(^block)();
- (void)eat;
@end
@implementation Person
- (void)eat
{
self.height += 1 ;
_block = ^{
// 在block的内部访问了自身这个对象。
NSLog(@"吃完饭后的体重是:%d",self.height);
};
_block();
}
@end
Person类外部使用:
// 这时候,p1这个强指针引着Person对象。
Person *p1 = [[Person alloc] init];
// eat内部调用block时,block就会强引用这person对象;
// 而本身person对象内部就强引着block。
[p1 eat];
// person对象和block两者互相强引着,谁也不能释放。
解决方案:
- (void)eat
{
self.height += 1 ;
//声明weakSelf弱指针(解决方案)
__weak typeof(self) weakSelf = self;
_block = ^{
NSLog(@"吃完饭后的体重是:%d",weakSelf.height);
};
_block();
}
更为复杂的情况:🏁 延迟操作或者异步任务 🏁
- (void)viewDidLoad
// 声明weakSelf弱指针
__weak typeof(self) weakSelf = self;
_block = ^{
//因为这里如果不用强指针的话,延时后,可能访问的这个对象就会销毁了,那么这个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();
}
网友评论