Block是一种特殊的数据类型
返回值(^block变量名)(形参列表) = ^( 形参列表){
};
无参数无返回值
void (^sunBlock)();
sunBlock = ^{
NSLog(@"sunBlock");
};
sunBlock();
有参数无返回值
void(^sunBlock)(int,int);
sunBlock = ^(int value1,int value2){
NSLog(@"%d",value1 + value2);
};
sunBlock(10,20);
有参数有返回值
int (^sunBlock)(int,int);
sunBlock = ^(int value1,int value2){
return value1 + value2;
};
NSLog(@"%d",sunBlock(10,20));
利用typedef给block起别名,和指向函数的指针一样,block变量的名称就是别名
typedef int (^calculateBlock)(int,int);
int main(int argc, const char * argv[]) {
calculateBlock sumBlock = ^(int value1,int value2){
return value1 + value2;
};
NSLog(@"%d",sumBlock(20,10));
calculateBlock minusBlock = ^(int value1,int value2){
return value1 - value2;
};
NSLog(@"%d",minusBlock(20,10));
}
int main(int argc, const char * argv[]) {
^{ };
return 0;
}
struct __block_impl {
void *isa; //isa,指向所属类的指针,也就是block的类型
int Flags; //flags,标志变量,在实现block的内部操作时会用到
int Reserved; //Reserved,保留变量
void *FuncPtr; //block执行时调用的函数指针
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
impl.isa = &_NSConcreteStackBlock; //__main_block_impl_0的isa指针指向了_NSConcreteStackBlock
impl.Flags = flags;
impl.FuncPtr = fp; //从main函数中看, __main_block_impl_0的FuncPtr指向了函数__main_block_func_0
Desc = desc; //__main_block_impl_0的Desc也指向了定义__main_block_desc_0时就创建的__main_block_desc_0_DATA,其中纪录了block结构体大小等信息。
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
}
static struct __main_block_desc_0 {
size_t reserved; //保留字段
size_t Block_size; //block大小(sizeof(struct __main_block_impl_0))
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
//以上代码在定义__main_block_desc_0结构体时,同时创建了__main_block_desc_0_DATA,并给它赋值,以供在main函数中对__main_block_impl_0进行初始化。
int main(int argc, const char * argv[]) {
((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
return 0;
}
1、__block_impl结构体,它包含了isa指针(包含isa指针的皆为对象),也就是说block也是一个对象 2、__main_block_impl_0结构体,可以看出是根据所在函数(main函数)以及出现序列(第0个)进行命名的,如果是全局的blcok,就根据变量名和出现序列进行命名。 3、__main_block_impl_0中包含了俩个成员变量和一个构造函数,成员变量分别是__block_impl结构体和描述信息Desc,之后在构造函数中初始化block的类型信息和函数指针等信息。 4、__main_block_func_0函数,其实对应的block的函数体,该函数接受了一个__cself参数,其实就是对应的block本身 5、__main_block_desc_0结构体,其中比较有价值的信息是block的大小 6、main函数对block的创建,可以看出执行block就是调用一个以block自身为参数的函数,这个函数对应着block的执行体。
void (^testGlobalBlock)() = ^{
NSLog(@"hello block");
};
int main(int argc, const char * argv[]) {
testGlobalBlock();
return 0;
}
testGlobalBlock的isa指向了_NSConcreteGlobalBlock,即在全局区域创建,block变量存储在全局数据存储区
int main(int argc, const char * argv[]) {
void (^testStackBlock)() = ^{
NSLog(@"hello block");
};
testStackBlock();
return 0;
}
testStackBlock的isa指向了_NSConcreteStackBlock,即在栈区创建。
int main(int argc, const char * argv[]) {
void (^testStackBlock)() = [^{
NSLog(@"hello block");
} copy];
testStackBlock();
return 0;
}
NSConcreteMallocBlock 类型的 block 通常不会在源码中直接出现,其需要由_NSConcreteStackBlock类型的block拷贝而来(也就是说block需要执行copy之后才能存放到堆中)。
其内部通过函数memmove将栈中的block的内容拷贝到了堆中,并使isa指向了_NSConcreteMallocBlock。
block主要的一些学问就出在栈中block向堆中block的转移过程中了。
int main(int argc, const char * argv[]) {
int testNum = 10;
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
打印结果:10
int main(int argc, const char * argv[]) {
int testNum = 10;
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNum = 20;
testNumBlock();
return 0;
}
打印结果:10
int main(int argc, const char * argv[]) {
int testNum = 10;
void(^testNumBlock)() = ^{
testNum = 20; //报错
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
__block int testNum = 10;
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNum = 20;
testNumBlock();
打印结果:20
int main(int argc, const char * argv[]) {
__block int testNum = 10;
void(^testNumBlock)() = ^{
testNum = 20;
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
打印结果:20
int testNum = 10;
int main(int argc, const char * argv[]) {
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
打印结果:10
int testNum = 10;
int main(int argc, const char * argv[]) {
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNum = 20;
testNumBlock();
return 0;
}
打印结果:20
int testNum = 10;
int main(int argc, const char * argv[]) {
void(^testNumBlock)() = ^{
testNum = 20;
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
打印结果:20
int main(int argc, const char * argv[]) {
static int testNum = 10;
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
打印结果:10
int main(int argc, const char * argv[]) {
static int testNum = 10;
void(^testNumBlock)() = ^{
NSLog(@"%d",testNum);
};
testNum = 20;
testNumBlock();
return 0;
}
打印结果:20
int main(int argc, const char * argv[]) {
static int testNum = 10;
void(^testNumBlock)() = ^{
testNum = 20;
NSLog(@"%d",testNum);
};
testNumBlock();
return 0;
}
打印结果:20
typedef void(^TestBlock)();
NSMutableArray *array;
void test(){
int a = 10;
TestBlock blcok = ^{
NSLog(@"%d",a);
};
[array addObject:blcok];
NSLog(@"%@",blcok);
}
int main(int argc, const char * argv[]) {
array = [[NSMutableArray alloc]init];
test();
TestBlock blockk = [array lastObject];
blockk();
NSLog(@"%@",blockk);
return 0;
}
结果:
在ARC下:
test2[2423:124143] <__NSMallocBlock__: 0x1004037f0>
test2[2423:124143] 10
test2[2423:124143] <__NSMallocBlock__: 0x1004037f0>
在非ARC下:
程序崩溃
test2[2449:125851] <__NSStackBlock__: 0x7fff5fbff6f8>
1、在非ARC下,TestBlock的isa指向NSStackBlock,当函数退出后,相应的堆被销毁,block也就不存在了,在经过copy或retain之后,对象的类型从NSStackBlock变为了NSMallocBlock,在函数结束后依然可以访问,在非ARC环境下,copy或retain了block后一定要在使用后release,不然会有内存泄露,而且泄露点是在系统级,在Instruments里跟不到问题触发点,比较上火。
2、ARC情况下,系统会将捕获了外部变量的block进行了copy。所以返回类型为NSMallocBlock,在函数结束后依然可以访问
如果把blcok中的代码不再访问变量:
TestBlock blcok = ^{
NSLog(@"demo");
};
结果:
ARC和非ARC得结果一致
test2[2484:128052] <__NSGlobalBlock__: 0x100005290>
test2[2484:128052] demo
test2[2484:128052] <__NSGlobalBlock__: 0x100005290>
- (testBlcok) myTestBlock {
__block int val = 10;
return ^{
NSLog(@"val = %d", val);
};
}
结果:Xcode就会提示报错Returning block that lives on the local stack
在向外传递block的时候一定也要做到,传给外面一个在堆上的,autorelease的对象。
- (testBlcok) myTestBlock {
__block int val = 10;
return [[^{
NSLog(@"val = %d", val);
} copy] autorelease];
}
- (testBlcok) myTestBlock {
__block int val = 10;
return ^{
NSLog(@"val = %d", val);
};
}
结果:正常
在ARC环境下,当block作为参数返回的时候,block也会自动被移到堆上。
ARC 和非ARC得声明一样
@property (strong, nonatomic) TestBlock *strongBlock;
@property (copy, nonatomic) TestBlock *copyBlock;
- (void)viewDidLoad {
[super viewDidLoad];
void(^testBlock)() = ^{
NSLog(@"------");
};
testBlock();
}
结果:当testBlock变量出了作用域,testBlock的内存会被自动释放
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
void(^testBlock)() = ^{
NSLog(@"%@",stu);
};
testBlock();
[stu release];
}
结果:Student可以正常释放
- (void)viewDidLoad {
[super viewDidLoad];
void(^testBlock)() = ^{
NSLog(@"testBlock");
};
testBlock();
Block_copy(testBlock);
Block_release(testBlock);
}
结果:Block正常释放
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
void(^testBlock)() = ^{
NSLog(@"%@",stu);
};
testBlock();
Block_copy(testBlock);
Block_release(testBlock);
[stu release];
}
结果:Student无法正常被释放,因为其在Block中被进行了一次retain操作
- (void)viewDidLoad {
[super viewDidLoad];
__block Student *stu = [[Student alloc]init];
void(^testBlock)() = ^{
NSLog(@"%@",stu);
};
testBlock();
Block_copy(testBlock);
Block_release(testBlock);
[stu release];
}
结果:Student可以正常释放
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
@end
----------------------------------------------
@implementation Student
-(void)dealloc{
NSLog(@"%s",__func__);
Block_release(_testBlock);
[super dealloc];
}
@end
----------------------------------------------
-(void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
stu.testBlock = ^{
NSLog(@"%@",stu);
};
stu.testBlock();
[stu release];
}
结果:因为testBlock作为Student的属性,采用copy修饰符修饰(这样才能保证Block在堆里面,以免Block在栈中被系统释放),所以Block会对Student对象进行一次retain操作,导致循环引用无法释放
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
-(void)resetBlock;
@end
----------------------------------------------
@implementation Student
-(void)resetBlock{
self.testBlock = ^{
NSLog(@"%@",self);
};
}
-(void)dealloc{
NSLog(@"%s",__func__);
Block_release(_testBlock);
[super dealloc];
}
@end
----------------------------------------------
-(void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
[stu resetBlock];
[stu release];
}
结果:Student对象在这里无法正常释放,虽然表面看起来一个alloc对应一个release符合内存管理规则,但是实际在resetBlock方法实现中,Block内部对self进行了一次retain操作,导致循环引用无法释放
如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用,解决循环引用的办法是在对象的前面使用下划线下划线block来修饰,以避免Block对对象进行retain操作
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
@end
----------------------------------------------
@implementation Student
-(void)dealloc{
NSLog(@"%s",__func__);
Block_release(_testBlock);
[super dealloc];
}
@end
----------------------------------------------
- (void)viewDidLoad {
[super viewDidLoad];
__block Student *stu = [[Student alloc]init];
stu.testBlock = ^{
NSLog(@"%@",stu);
};
stu.testBlock();
[stu release];
}
结果:Student可以正常释放
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
-(void)resetBlock;
@end
----------------------------------------------
@implementation Student
-(void)resetBlock{
// 这里为了通用一点,可以使用__block typeof(self) stu = self;
__block Student *stu = self;
self.testBlock = ^{
NSLog(@"%@",stu);
};
}
-(void)dealloc{
NSLog(@"%s",__func__);
Block_release(_testBlock);
[super dealloc];
}
@end
----------------------------------------------
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
[stu resetBlock];
[stu release];
}
结果:Student可以正常释放
- (void)viewDidLoad {
[super viewDidLoad];
void(^testBlock)() = ^{
NSLog(@"testBlock");
};
testBlock();
}
结果:当Block变量出了作用域,Block的内存会被自动释放
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
void(^testBlock)() = ^{
NSLog(@"%@",stu);
};
testBlock();
}
结果:Student可以正常释放
如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
@end
----------------------------------------------
@implementation Student
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
----------------------------------------------
-(void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
stu.testBlock = ^{
NSLog(@"%@",stu);
};
stu.testBlock();
}
结果:因为testBlock作为Student的属性,采用copy修饰符修饰(这样才能保证Block在堆里面,以免Block在栈中被系统释放),所以Block会对Person对象进行一次强引用,导致循环引用无法释放
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
- (void)resetBlock;
@end
----------------------------------------------
@implementation Student
- (void)resetBlock
{
self.testBlock = ^{
NSLog(@"------%@", self);
};
}
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
----------------------------------------------
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
[stu resetBlock];
}
结果:Student对象在这里无法正常释放,在testBlock方法实现中,Block内部对self进行了一次强引用,导致循环引用无法释放
如果对象内部有一个Block属性,而在Block内部又访问了该对象,那么会造成循环引用,解决循环引用的办法是使用一个弱引用的指针指向该对象,然后在Block内部使用该弱引用指针来进行操作,这样避免了Block对对象进行强引用
第一种情况
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
@end
----------------------------------------------
@implementation Student
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
----------------------------------------------
- (void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
__weak typeof(stu) weakS = stu;
stu.testBlock = ^{
NSLog(@"------%@", weakS);
};
stu.testBlock();
// Student对象在这里可以正常被释放
}
第二种情况
@interface Student : NSObject
@property (nonatomic,copy) void(^testBlock)();
-(void)resetBlock;
@end
----------------------------------------------
@implementation Student
-(void)resetBlock
{
//这里为了通用一点,可以使用__weak typeof(self) weakP = self;
__weak Student *stu = self;
self.testBlock = ^{
NSLog(@"------%@", self);
};
}
-(void)dealloc{
NSLog(@"%s",__func__);
}
@end
----------------------------------------------
-(void)viewDidLoad {
[super viewDidLoad];
Student *stu = [[Student alloc]init];
[stu resetBlock];
}
结果:Student可以正常释放
注: 关于下划线下划线block关键字在MRC和ARC下的不同
__block在MRC下有两个作用
__block在ARC下只有一个作用