转载:http://blog.csdn.net/xieexiaozhang/article/details/52024065
1.什么是Block?
Block是属于C语言框架
Block是一种数据类型(类似int)
Block是一段代码块,只有在被调用的时候被执行(类似函数和方法)
Block可以定义成临时变量
Block可以定义成参数
Block可以定义成属性
2.Block的语法结构
1.无参数,无返回值
/*
数据类型 : void(^)()
变量名 : task1
值 : ^{ NSLog(@"task1"); };
*/
void(^task1)() = ^{
NSLog(@"task1");
};
2.有参数,无返回值
// 参数类型(如果有形参,这个可省) 形参
void(^task2)(NSString *) = ^(NSString *str){
NSLog(@"task2 %@",str);
};
3.无参数,有返回值
int(^task3)() = ^{
return 1;
};
4.有参数,有返回值
int(^task4)() = ^(int a,int b){
return a + b;
};
3.ARC下,Block的内存空间
结论:
1.ARC环境下,单纯的定义一个block存储在全局区 <__NSGlobalBlock__: 0x1045c60b0>
示例代码
- (void)blockDemo
{
void(^task1)() = ^{
NSLog(@"task");
};
task1();
// block的本质是指针对象
// ARC环境下,单纯的定义一个block存储在全局/常量区 <__NSGlobalBlock__: 0x10e80f100>
NSLog(@"task1==%@",task1);
}
控制台打印:
2016-06-27 08:55:51.263 Block使用[732:22600] task
2016-06-27 08:55:51.264 Block使用[732:22600] task1==<__NSGlobalBlock__: 0x10e80f100>
4.ARC下,Block内访问外部变量的内存变化
结论:
前提是ARC : Block访问外部的变量
2.ARC环境下,block访问外部的变量时存储在堆区 <__NSMallocBlock__: 0x7fa8fd00c1b0>
2.1 在block访问这个变量之前,变量在栈区 == 0x7fff53c73bfc
2.2 在block内部访问这个变量时,变量会被block拷贝到堆区 0x7fe851517880
示例代码:
#pragma mark - ARC - Block访问外部的变量
// 需求 : 研究block和外部变量的内存的变化
- (void)blockDemo1
{
int num = 10;
// 在block访问这个变量之前,变量在栈区 == 0x7fff52544bec
NSLog(@"num01==%p",&num);
void(^task1)() = ^{
// 在block内部访问这个变量时,变量会被block拷贝到堆区 0x7f8108e0d620
NSLog(@"task1 %d %p",num,&num);
};
task1();
// block的本质是指针对象
// ARC环境下,block访问外部的变量时存储在堆区 <__NSMallocBlock__: 0x7f8108e0d600>
NSLog(@"task1==%@",task1);
// 当block在其内部使用完了外部的变量之后,这个变量又会重新回到栈区 0x7fff52544bec
NSLog(@"num02==%p",&num);
}
控制台打印日志:
2016-06-27 08:59:39.334 Block使用[753:25411] num01==0x7fff52544bec
2016-06-27 08:59:39.334 Block使用[753:25411] task1 10 0x7f8108e0d620
2016-06-27 08:59:39.334 Block使用[753:25411] task1==<__NSMallocBlock__: 0x7f8108e0d600>
2016-06-27 08:59:39.334 Block使用[753:25411] num02==0x7fff52544bec
5.ARC下,Block内修改外部变量的内存变化
结论:
前提是ARC : Block修改外部的变量
1.ARC环境下,当block修改外部变量的时候,会在堆区 <__NSMallocBlock__: 0x7f82ac8a6130>
2.在block的外面,即使你使用__block修饰了,那么他的地址依然不变,在栈区 0x7fff5e101bf8
3.在block内部修改外部的变量时,使用__block修饰了外部的变量之后,外部的变量会在堆区0x7f9d10e0bcc8
示例代码
#pragma mark - ARC - Block修改外部的变量
// 需求 : 研究block和外部变量的内存的变化
- (void)blockDemo2
{
__block int num = 10;
// 在block的外面,即使你使用__block修饰了,那么他的地址依然不变,在栈区 0x7fff57337be8
NSLog(@"num01==%p",&num);
void(^task2)() = ^{
/*
提示 : 在block内部修改外部变量是不被允许的
如果非要修改,那么久需要把外部的变量用 __block 来修饰
*/
num = 20;
// 在block内部修改外部的变量时,使用__block修饰了外部的变量之后,外部的变量会在堆区 0x7fd3ea443ad8
NSLog(@"task2 %d =%p",num,&num);
};
task2();
// 当block修改外部变量的时候,会在堆区 <__NSMallocBlock__: 0x7fd3ea438d70>
NSLog(@"task2%@",task2);
// 当block内部修改完外部的变量之后,那么这个变量的就会保存到堆区 0x7fd3ea443ad8
NSLog(@"num02==%p",&num);
}
控制台打印日志
2016-06-27 09:04:17.459 Block使用[773:28467] num01==0x7fff57337be8
2016-06-27 09:04:17.460 Block使用[773:28467] task2 20 =0x7fd3ea443ad8
2016-06-27 09:04:17.460 Block使用[773:28467] task2<__NSMallocBlock__: 0x7fd3ea438d70>
2016-06-27 09:04:17.460 Block使用[773:28467] num02==0x7fd3ea443ad8
6.MRC下,Block的内存位置
结论:
1.MRC环境下,单纯的定义一个block存储在全局区 <__NSGlobalBlock__: 0x1045c60b0>
示例代码
- (void)blockDemo1
{
void(^task1)() = ^{
};
task1();
// 全局/常量区 <__NSGlobalBlock__: 0x10ddf10f0>
NSLog(@"%@",task1);
}
控制台信息
2016-06-27 09:42:17.813 MRC下Block内存管理[973:45271] <__NSGlobalBlock__: 0x10ddf10f0>
7.MRC下,Block的访问外部变量的内存变化
结论:
1.MRC环境下,Block访问外部变量的时候存放在栈区。 <__NSStackBlock__: 0x7fff58e23b08>
a.Block访问外部变量时,外部变量在栈区,地址 0x7fff58e23b3c
b.Block访问外部变量时,外部变量依然在栈区,但是地址发生了变化 0x7fff58e23b28
c.Block访问结束以后。外部变量依然在栈区,并且地址和原始的一致。
示例代码
- (void)blockDemo2
{
int num = 10;
// 变量的内存在栈区 0x7fff58e23b3c
NSLog(@"num01==%p",&num);
void(^task2)() = ^{
// 外部的变量在block内部依然在栈区,但是地址变了 0x7fff58e23b28 (地址变化了,内存空间没变)
NSLog(@"task2 %d %p",num,&num);
};
task2();
// block存储在栈区 <__NSStackBlock__: 0x7fff58e23b08>
NSLog(@"%@",task2);
// 变量依然在栈区,并且地址和开始时的一样 0x7fff58e23b3c
NSLog(@"num02==%p",&num);
}
控制台信息
2016-06-27 09:47:52.118 MRC下Block内存管理[987:48208] num01==0x7fff58e23b3c
2016-06-27 09:47:52.119 MRC下Block内存管理[987:48208] task2 10 0x7fff58e23b28
2016-06-27 09:47:52.119 MRC下Block内存管理[987:48208] <__NSStackBlock__: 0x7fff58e23b08>
2016-06-27 09:47:52.119 MRC下Block内存管理[987:48208] num02==0x7fff58e23b3c
8.MRC下,Block修改外部变量的内存变化
结论:
1.MRC环境下,Block修改外部变量时存放在栈区<__NSStackBlock__: 0x7fff57e2cae0>
a.Block修改外部变量前,外部变量存放在栈区
b.Block修改外部变量时,外部变量依然存放在栈区,但是地址发生了变化。
c.Block修改外部变量之后,外部变量的地址已经发生变化,已经不是原始值了。
示例代码
- (void)blockDemo3
{
__block int num = 10;
// 在block访问之前,外部变量存放在栈区 0x7fff57e2cb38
NSLog(@"num01==%p",&num);
void (^task3)() = ^{
num = 20;
//在block修改时,外部变量依然存放在栈区,但是地址发生了变化, 0x7fff57e2cb38
NSLog(@"task3 %d %p",num,&num);
};
task3();
// block的地址存放在栈区 <__NSStackBlock__: 0x7fff57e2cae0>
NSLog(@"%@",task3);
// 外部变量在block内部修改以后,地址为修改时的地址,依然存放在栈区 0x7fff57e2cb38
NSLog(@"num02==%p",&num);
}
控制台信息
2016-06-27 10:00:01.069 MRC下Block内存管理[1006:53704] num01==0x7fff57e2cb38
2016-06-27 10:00:01.069 MRC下Block内存管理[1006:53704] task3 20 0x7fff57e2cb38
2016-06-27 10:00:01.069 MRC下Block内存管理[1006:53704] <__NSStackBlock__: 0x7fff57e2cae0>
2016-06-27 10:00:01.070 MRC下Block内存管理[1006:53704] num02==0x7fff57e2cb38
8.为什么Block属性需要使用copy修饰
a.在ARC下,使用strong和copy都是一样的,因为在访问/修改外部变量的时候,block都是在堆区,苹果官方建议使用copy
b.在MRC下,单纯的Block是存放在全局/常量区的,如果Block访问/修改外部变量后,block存放在了栈区,在栈区是不可以全局共享的,只有堆区的对象,变量才会被全局共享,所以使用copy拷贝一份Block到堆区中,这样Block才会全局共享。