1、block类型

block有三种类型分别是NSGlobalBlockNSMallocBlockNSStackBlock,内存分配如下图所示:
image.png

各个分区内存特点: 程序区域:代码段,存放程序代码 数据区域:存放全局变量 堆:动态分配内存,存储alloc或者malloc出来的对象,需要开发者申请内存,也需要开发者管理内存 栈:存放局部变量,系统会自动分配内存,系统会自动管理内存 具体可参考 iOS程序的内存布局

思考:Class对象保存在内存中什么位置?
通过打印各个类型变量地址观察:

  1. // 全局变量,存放在数据区
  2. int a = 1;
  3. void test2() {
  4. // 局部变量,存放在栈
  5. int b = 2;
  6. // alloc出来的对象,存放在堆
  7. NSObject *obj = [[NSObject alloc] init];
  8. // 打印地址
  9. NSLog(@"a %p",&a);
  10. NSLog(@"b %p",&b);
  11. NSLog(@"obj %p",obj);
  12. NSLog(@"NSObject class %p",[NSObject class]);
  13. NSLog(@"NSArray class %p",[NSArray class]);
  14. NSLog(@"Person class %p",[Person class]);
  15. NSLog(@"Dog class %p",[Dog class]);
  16. }

打印结果:

  1. ~: a 0x100008228
  2. ~: b 0x7ffeefbff41c
  3. ~: obj 0x1007266b0
  4. ~: NSObject class 0x7fff807dd088
  5. ~: NSArray class 0x7fff8024d510
  6. ~: Person class 0x1000081f8
  7. ~: Dog class 0x1000081a8

通过观察可知,系统类NSObject和NSArray的地址和b相似,保存在栈;自定义类Person、Dog的类对象地址和a相似,所以保存在数据区。

2、block类型特点

image.png
如果创建了一个block,但是没有访问auto变量,block是NSGlobalBlock类型:

  1. void (^block1)(void) = ^ {
  2. };

如果创建了一个block,访问了auto变量,block是NSStackBlock类型:

  1. int age = 10;
  2. void (^block2)(void) = ^{
  3. NSLog(@"age = %d",age);
  4. };

*需要在MRC环境下验证,在ARC环境下,编译器可能会将栈上的block复制到堆上

如果一个NSStackBlock类型block,调用了copy方法,block变成NSMallocBlock类型:

  1. int age = 10;
  2. void (^block3)(void) = [^{
  3. NSLog(@"age = %d",age);
  4. } copy];

各个类型block调用copy的结果:
image.png

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属性的建议写法:

  1. @property (copy, nonatomic) void (^block)(void);

ARC下block属性的建议写法:

  1. @property (strong, nonatomic) void (^block)(void);
  2. @property (copy, nonatomic) void (^block)(void);