Hello, World!

Block基础

字数统计: 972阅读时长: 4 min
2017/11/08 Share

什么是block

block就是一个里面存储了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息结构体

block内存分布

三种类型block

在Objective-C语言中,有三种类型的block。
1,__NSGlobalBlock__ 保存在全局区,不会访问任何外部变量。
2,__NSStackBlock__ 保存在栈中,当函数返回时会被销毁。
3,__NSMallocBlock__ 保存在堆中,当引用计数为0时会被销毁。

在MRC模式下,运行如下代码,从log中可以证明存在三种类型的block。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
- (void)viewDidLoad {
[super viewDidLoad];

// 一
void(^globalBlock)(void) = ^{
NSLog(@"hello world!");
};
NSLog(@"%s", object_getClassName(globalBlock));

// 二
NSInteger i = 10;
void(^stackBlock)(void) = ^{
NSLog(@"stackBlock %zd", i);
};
NSLog(@"%s", object_getClassName(stackBlock));

// 三
void(^mallocBlock)(void) = [stackBlock copy];
NSLog(@"%s", object_getClassName(mallocBlock));

}

log信息:

1
2
3
__NSGlobalBlock__
__NSStackBlock__
__NSMallocBlock__

对三种类型的block实例发送消息
block1

如何判断block类型

我们知道有三种类型block,那么如何判断一个block是属于哪种的呢?依据如下规则即可。

  • 没有使用外部变量的(外部变量不包括全局变量和静态变量)block,在全局区,属于__NSGlobalBlock__。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    // 在MRC模式下运行
    typedef void(^TestBlock)(void);
    int globalVal = 666;
    static int staticVal = 888;

    @interface DemoViewController ()

    @property(nonatomic, strong)TestBlock strongBlock;
    @property(nonatomic, copy)TestBlock copyBlock;
    @property(nonatomic, weak)TestBlock weakBlock;

    @end

    @implementation DemoViewController

    - (void)viewDidLoad {
    [super viewDidLoad];

    _strongBlock = ^{
    NSLog(@"hello world!");
    };
    _copyBlock = ^{
    NSLog(@"_copyBlock %d", globalVal);
    };
    _weakBlock = ^{
    NSLog(@"_weakBlock %d", staticVal);
    };

    NSLog(@"_strongBlock %s", object_getClassName(_strongBlock));
    NSLog(@"_copyBlock %s", object_getClassName(_copyBlock));
    NSLog(@"_weakBlock %s", object_getClassName(_weakBlock));
    }

    @end

    log信息显示如下:

    1
    2
    3
    _strongBlock __NSGlobalBlock__
    _copyBlock __NSGlobalBlock__
    _weakBlock __NSGlobalBlock__
  • 使用外部变量的(外部变量不包括全局变量和静态变量)block,在栈区,属于__ NSStackBlock__。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 在MRC模式下运行
    typedef void(^TestBlock)(void);

    @interface DemoViewController ()

    @property(nonatomic, strong)TestBlock strongBlock;
    @property(nonatomic, copy)TestBlock copyBlock;
    @property(nonatomic, weak)TestBlock weakBlock;

    @end

    @implementation DemoViewController

    - (void)viewDidLoad {
    [super viewDidLoad];

    int tempV = 100;
    _strongBlock = ^{
    NSLog(@"_strongBlock %d", tempV);
    };
    _copyBlock = ^{
    NSLog(@"_copyBlock %d", tempV);
    };
    _weakBlock = ^{
    NSLog(@"_weakBlock %d", tempV);
    };

    NSLog(@"_strongBlock %s", object_getClassName(_strongBlock));
    NSLog(@"_copyBlock %s", object_getClassName(_copyBlock));
    NSLog(@"_weakBlock %s", object_getClassName(_weakBlock));
    }

    @end

    log信息显示如下:

    1
    2
    3
    _strongBlock __NSStackBlock__
    _copyBlock __NSStackBlock__
    _weakBlock __NSStackBlock__
  • 对栈区block进行copy之后,得到新的block,在堆区,属于__ NSMallocBlock__。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 在MRC模式下运行
    typedef void(^TestBlock)(void);

    @interface DemoViewController ()

    @property(nonatomic, strong)TestBlock strongBlock;
    @property(nonatomic, copy)TestBlock copyBlock;
    @property(nonatomic, weak)TestBlock weakBlock;

    @end

    @implementation DemoViewController

    - (void)viewDidLoad {
    [super viewDidLoad];

    int tempV = 100;
    _strongBlock = ^{
    NSLog(@"_strongBlock %d", tempV);
    };
    _copyBlock = ^{
    NSLog(@"_copyBlock %d", tempV);
    };
    _weakBlock = ^{
    NSLog(@"_weakBlock %d", tempV);
    };

    NSLog(@"_strongBlock copy %s", object_getClassName([_strongBlock copy]));
    NSLog(@"_copyBlock copy %s", object_getClassName([_copyBlock copy]));
    NSLog(@"_weakBlock copy %s", object_getClassName([_weakBlock copy]));
    }

    @end

    log信息显示如下:

    1
    2
    3
    _strongBlock copy __NSMallocBlock__
    _copyBlock copy __NSMallocBlock__
    _weakBlock copy __NSMallocBlock__

ARC对block的影响

ARC会自动把strong类型且捕获外部变量的block从栈copy到堆中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 在ARC模式下运行
typedef void(^TestBlock)(void);

@interface DemoViewController ()

@property(nonatomic, strong)TestBlock strongBlock;
@property(nonatomic, copy)TestBlock copyBlock;

@end

@implementation DemoViewController

- (void)viewDidLoad {
[super viewDidLoad];

NSInteger i = 10;
//
_strongBlock = ^{
NSLog(@"%zd", i);
};
NSLog(@"_strongBlock %s", object_getClassName(_strongBlock));
//
_copyBlock = ^{
NSLog(@"%zd", i);
};
NSLog(@"_copyBlock %s", object_getClassName(_copyBlock));
//
void(^_localBlock)(void) = ^{
NSLog(@"%zd", i);
};
NSLog(@"_localBlock %s", object_getClassName(_localBlock));

}

@end

log信息显示如下:

1
2
3
_strongBlock __NSMallocBlock__
_copyBlock __NSMallocBlock__
_localBlock __NSMallocBlock__

根据log信息可以判断strong、copy修饰的或者局部变量引用的block实例,都属于__NSMallocBlock__。

原因:_strongBlock、_copyBlock和_localBlock都使用了外部变量,属于栈区block,但是在ARC模式下,被strong或copy等非weak修饰符修饰的栈区block,会自动copy到堆区,变成__NSMallocBlock__。(block实例赋值给局部变量_localBlock,就相当于被变量强引用=strong,所以_localBlock是__NSMallocBlock__)

变量的复制

对于 block 外的变量引用,block 默认是将其复制到其数据结构中来实现访问的,如果对象是引用类型,则block会将其引用计数加一 ,如下图所示:
block2
对于用 __block 修饰的外部变量引用,block 是复制其引用地址来实现访问的,如下图所示:
block3

CATALOG
  1. 1. 什么是block
  2. 2. block内存分布
    1. 2.1. 三种类型block
    2. 2.2. 如何判断block类型
    3. 2.3. ARC对block的影响
  3. 3. 变量的复制