1、block类型
block有三种类型分别是NSGlobalBlock、NSMallocBlock和NSStackBlock,内存分配如下图所示:
各个分区内存特点: 程序区域:代码段,存放程序代码 数据区域:存放全局变量 堆:动态分配内存,存储alloc或者malloc出来的对象,需要开发者申请内存,也需要开发者管理内存 栈:存放局部变量,系统会自动分配内存,系统会自动管理内存 具体可参考 iOS程序的内存布局
思考:Class对象保存在内存中什么位置?
通过打印各个类型变量地址观察:
// 全局变量,存放在数据区int a = 1;void test2() {// 局部变量,存放在栈int b = 2;// alloc出来的对象,存放在堆NSObject *obj = [[NSObject alloc] init];// 打印地址NSLog(@"a %p",&a);NSLog(@"b %p",&b);NSLog(@"obj %p",obj);NSLog(@"NSObject class %p",[NSObject class]);NSLog(@"NSArray class %p",[NSArray class]);NSLog(@"Person class %p",[Person class]);NSLog(@"Dog class %p",[Dog class]);}
打印结果:
~: a 0x100008228~: b 0x7ffeefbff41c~: obj 0x1007266b0~: NSObject class 0x7fff807dd088~: NSArray class 0x7fff8024d510~: Person class 0x1000081f8~: Dog class 0x1000081a8
通过观察可知,系统类NSObject和NSArray的地址和b相似,保存在栈;自定义类Person、Dog的类对象地址和a相似,所以保存在数据区。
2、block类型特点

如果创建了一个block,但是没有访问auto变量,block是NSGlobalBlock类型:
void (^block1)(void) = ^ {};
如果创建了一个block,访问了auto变量,block是NSStackBlock类型:
int age = 10;void (^block2)(void) = ^{NSLog(@"age = %d",age);};
*需要在MRC环境下验证,在ARC环境下,编译器可能会将栈上的block复制到堆上
如果一个NSStackBlock类型block,调用了copy方法,block变成NSMallocBlock类型:
int age = 10;void (^block3)(void) = [^{NSLog(@"age = %d",age);} copy];
3、Block中的copy
在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上,比如以下情况:
1、block作为函数返回值时
2、将block赋值给__strong指针时
3、block作为Cocoa API中方法名含有usingBlock的方法参数时
4、block作为GCD API的方法参数时
5、block访问了auto变量
4、总结
block分类三种类型,通过是否访问auto变量和调用copy决定,不同类型的block调用copy效果不同。
平时使用block时一般会用copy修饰,因为放在栈区的block的内存由系统管理,调用block时可能出现内部存储的数据错乱问题,所以需要拷贝到堆区进行使用。
MRC下block属性的建议写法:
@property (copy, nonatomic) void (^block)(void);
ARC下block属性的建议写法:
@property (strong, nonatomic) void (^block)(void);@property (copy, nonatomic) void (^block)(void);
