块的内部结构
<<编写高质量代码的52个有效方法>>中摘抄如下:
OC中的Block的三种类型
OC中,一般Block就分为以下3种:
-
_NSConcreteStackBlock
特点:存放在栈区,生命周期由系统控制的,一旦返回之后,就被系统销毁了。
栈block的表现形式:- 条件1:拥有局部变量(自动变量)或者成员属性变量(即使被strong或者copy修饰的成员变量)
- 没有被强引用
- 如下面的例1:
-
_NSConcreteGlobalBlock
特点:这种Block存储在程序的数据区域(跟函数存储在一起),生命周期从创建到应用程序结束。全局block的copy是个空操作,实际上就相当于单粒.
全局block的表现形式:- block中没有用到任何block内部以外的变量
- block内部仅仅用到了全局变量/静态全局变量,静态变量
- 如下面的例2
-
_NSConcreteMallocBlock:
特点:存放在堆区,没有强指针引用即销毁,生命周期由程序员控制
堆block的表现形式:
堆中的block无法直接创建,其需要由_NSConcreteStackBlock类型的block拷贝而来(也就是说block需要执行copy之后才能存放到堆中)
在ARC环境下,以下几种情况,编译器会自动的判断,把Block自动的从栈copy到堆- 手动调用copy的栈block
- 栈Block被强引用,被赋值给__strong或者id类型
- copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock,这可以归纳为上一条
- Block是函数的返回值
- 调用系统API入参中含有usingBlcok的方法
- 举例3:
例1:stackBlock
#import "TestObject.h"
@interface TestObject ()
{
int b;
}
@property (nonatomic,copy) NSString *str;
@end
@implementation TestObject
- (void)test {
int i = 10;
//注意,这个block可是srong类型,OC中定义一个变量默认情况下是__strong类型
//堆block
// void (^block)() = ^{i;};
//栈block
__weak void (^weakBlock)() = ^{i;};
// 创建时,都会在栈中
NSLog(@"%@", ^{i;});
// 获取了该对象的成员属性变量
NSLog(@"%@", ^{b;});
//被copy修饰的str
NSLog(@"=====%@", ^{ _str;} );
// 如果是weak类型的block
NSLog(@"%@", weakBlock);
}
@end
打印结果如下:
<__NSStackBlock__: 0x7fff5b3600f0>
<__NSStackBlock__: 0x7fff5b3600b8>
<__NSStackBlock__: 0x7fff5b360118>
=====<__NSStackBlock__: 0x7fff5594c0a8>
例2:两种globalBlock
#import <Foundation/Foundation.h>
int global_i = 1;
static int static_global_j = 2;
int main(int argc, const char * argv[]) {
static int static_k = 3;
//globalBlock1:类型1
void (^myBlock)(void) = ^{
NSLog(@"Block中 变量 = %d %d %d",static_global_j ,static_k, global_i);
};
myBlock();
NSLog(@"myBlock===%@",myBlock);
//globalBlock2:类型2
void (^block2)(void) = ^{
};
NSLog(@"block2===%@",block2);
return 0;
}
打印结果:
Block中 变量 = 2 3 1
myBlock===<__NSGlobalBlock__: 0x10cc6b050>
block2===<__NSGlobalBlock__: 0x10cc6b090>
例3:堆block
#import "TestObject.h"
@interface TestObject ()
{
int b;
}
@property (nonatomic,copy) void (^tesblock)();
@property (nonatomic,copy) NSString *str;
@end
@implementation TestObject
- (void)test {
int i = 10;
// 1.手动调用copy的栈block
NSLog(@"%@", [^{i;} copy]);
//注意,这个block可是srong类型,OC中定义一个变量默认情况下是__strong类型
//2.栈Block被强引用,被赋值给__strong或者id类型
void (^block)() = ^{i;};
NSLog(@"block===%@", block);
//3.copy修饰的成员属性引用的block会被复制一份到堆中成为MallocBlock
self.tesblock = ^{
//如果没有这个i,那就是全局block,即使用copy引用,也是全局block,从而也说明全局block是不可囊转化成堆block
i;
};
NSLog(@"tesblock===%@", self.tesblock);
//4.Block是函数的返回值
NSLog(@"block3===%@", [self test3]);
//5.调用系统API入参中含有usingBlcok的方法(这个还不懂,以后有空在怼吧)
}
- (void(^)())test3 {
//如果是全局block那么,即使是函数的返回值,他仍然还是全局block
//return ^{};
int a;
return ^{a;};
}
@end
打印结果:
<__NSMallocBlock__: 0x6000002425b0>
block===<__NSMallocBlock__: 0x60400025c1a0>
tesblock===<__NSMallocBlock__: 0x60400025c290>
block3===<__NSMallocBlock__: 0x60400025c3b0>
block作为函数的参数
当Block为函数参数的时候,需要我们手动copy一份到堆上
- (void)test {
int i = 10;
[self test4:^{i;}];
}
- (void)test4:(void (^)())sblock {
//sblock是一个栈block,需要copy操作
NSLog(@"sblock===%@", [sblock copy]);
}