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);