带有自动变量(局部变量)的匿名函数。(Block其实就是一个代码块,把你想要执行的代码封装在这个代码块里,等到需要时再去调用。)
ARC下block如果访问外部变量,block在堆里,可以使用copy和strong,并且block是一个对象。
定义属性:
void (^blockName)( );//无返回值,无参数
void (^blockName)(NSObject,string* name);//无返回值,有参数
NSString* (^blockName)(int age,NSString* name)
声明:
void (^blockName)(NSString* x,int y);
block赋值:
block = ^返回值类型(参数列表) {函数主体}
利用typedef为block重新命名
typedef int (^aaa)(int a,int b);
1.Block内访问局部变量:在Block定义时便是将局部变量的值传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改并不会影响Block内部的值,同时内部的值也是不可修改的.
//block访问普通局部变量,是传的a的值,值传递
- (void)test1
{
int a = 10;
void (^block)() = ^{
NSLog(@"a is %d",a); //10 传的a的值
};
a = 20;
block();
NSLog(@"a2 is %d",a); //20
}
2.Block内访问__block修饰的局部变量: 在局部变量前使用__block修饰,在Block定义时便是将局部变量的指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。(block块内只读)
//__block static修饰,全局变量,指针传递,传的地址。
- (void)test2
{
__block int a = 10;
void (^block)() = ^{
NSLog(@"a is %d",a); //20 传的a的地址&a
};
a = 20;
block();
NSLog(@"a is %d",a); //20
}
3、Block内访问全局变量:全局变量所占用的内存只有一份,供所有函数共同调用,在Block定义时并未将全局变量的值或者指针传给Block变量所指向的结构体,因此在调用Block之前对局部变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
int a = 10;
- (void)test3
{
void (^block)() = ^{
NSLog(@"a is %d",a); //20 传的a的地址&a
};
a = 20;
block();
NSLog(@"a is %d",a); //20
}
4、Block内访问静态变量: 在Block定义时便是将静态变量的指针传给Block变量所指向的结构体,因此在调用Block之前对静态变量进行修改会影响Block内部的值,同时内部的值也是可以修改的。
- (void)test4
{static int a = 10;
void (^block)() = ^{
NSLog(@"a is %d",a); //20 传的a的地址&a
};
a = 20;
block();
NSLog(@"a is %d",a); //20
}
Block的内部:
1. _ _block int b = 20; //带_ _block修饰符的block普通变量。
//每个block变量都会生成一个和OC类内存结构兼容的结构体。下面是_block int b 的结构体定义:
struct Block_b {
void *isa; //固定为NULL
Block_b *forwarding; //指向真正的block对象变量。
int flags;
int size; //结构体的size
int b; //保存代码中定义的变量值。
};
2. _ _block NSString *blockStr = str; //带__block修饰符block OC变量。
//每个block变量都会生成一个和OC类内存结构兼容的结构体。下面是 __block NString *blockStr 的结构体定义:
struct Block_blockStr {
void *isa; //固定为NULL
Block_blockStr *forwarding; //指向真正的block对象变量
int flags;
int size; //结构体的size
NSString * blockStr; //保存代码中定义的变量值。
};
3.//定义一个block块并带一个参数
void (^testBlock)(int) = ^(int c){
int d = a + b + c;
NSLog(@"d=%d, strongStr=%@, blockStr=%@, weakStr=%@", d, strongStr, blockStr, weakStr);
};
//每个block块都会生成一个和OC类兼容的结构体。
struct Block_testBlock {
//前面5个数据成员在所有block定义中都相同,并且和OC兼容。
Class isa;
int flags;
int reserved;
void *funcPtr; //block块的全局函数的地址。
Block_testBlock_Desc* desc; //block的描述。
//所有在block代码块引用的外部数据都会成为结构体的同名数据成员。
int a;
NSString * strongStr;
NSString * __weak weakStr;
Block_b *b;
Block_blockStr *blockStr;
//结构体的构造函数。
Block_testBlock(void *_funcPtr, Block_testBlock_Desc *_desc, int _a, NSString * _strongStr, NSString * _weakStr, struct Block_b *_b, Block_blockStr *_blockStr, int _flags)
{
isa = &_NSConcreteStackBlock; //根据具体的block类型赋值。
flags = _flags;
reserved = 0;
funcPtr = _funcPtr;
desc = _desc;
a = _a;
strongStr = _strongStr;
weakStr = _weakStr;
b = _b->forwarding; //其实就是指向自己
blockStr = _blockStr->forwarding;
}
};
//每个block块的描述信息结构体,主要是保存block的尺寸,以及block中函数的参数信息。
struct Block_testBlock_Desc {
unsigned long reserved;
unsigned long size; //块的尺寸
void *rest[1]; //块的参数签名信息
}_testBlock_desc_DATA = {0, sizeof(Block_testBlock), "v12@?0i8"};
看出我们定义的block代码块都会生成2个结构体:
Block_testBlock用来保存block的信息以及block内部要用到的所有数据。所有block对象结构体的前5个数据成员都是一致的,也就是和OC类的内存结构是兼容的。其中的isa用来保存block的类信息,这里面的类信息会根据block所处的位置的不同而不同。而后面的5个数据成员就是在block代码块内使用外部对象的副本。正是因为每个block对象在编译时保存了代码块内使用代码块外的对象的副本,所以我们才能在后续代码执行时能够访问到这些信息。
Block_testBlock_Desc用来描述这个block的size以及block方法的参数的签名信息。
注意:
1.对于基本类型a的副本来说就是完全的内存拷贝,因此在block代码块内更新这些数据是不会影响到外面,同时外面的更新也不会影响到里面了。
2.对于对象类型的strongStr和weakStr而言这个副本只是指针的拷贝而不是所指对象的拷贝,因此在block代码块内能够读取最新的属性和设置新的属性值。
3.对于__block类型的对象来说,你会发现他也是指针的拷贝,所以也不会产生多份内存副本,同时可以看出对__block类型数据的读取和设置我们都是间接来完成的,因此这里代码块内更新数据能影响外面,同时外面的更新也能影响里面。
block的循环引用:self->block->self
1. 如果要在block中直接使用外部强指针会发生错误。
__weak typeof(self) weakSelf = self;
2. 如果在block内部使用延时操作还使用弱指针的话,会取不到该指针,需要在block内部在讲弱指针强引用一下。
__strong typeof(self) strongSelf = weakSelf;
block的用途:
1.页面间传值和回调比较方便。
2.Block与多线程结合。
3.视频动画的一些API
4. 网络请求的回调
5.集合的遍历,如地区列表排序
6. 用闭包可以解决那些执行逻辑和上下文环境解耦的场景。