block分为三种类型:
- __NSStackBlock
- __NSMallocBlock
- __NSGlobalBlock
不同类型的block储存在内存上的位置也不相同。内存分布:
image1.png
MRC下
- 对于没有引用外界变量的block(不管用什么修饰的)都为
__NSGlobalBlock
,例子:
@property (nonatomic, copy) void (^copyBlock)(void);
@property (nonatomic, retain) void (^retainBlock)(void);
- (void)testBlock {
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d", a + b);
};
NSLog(@"normalBlock %@", normalBlock);
self.copyBlock = ^() {
NSLog(@"copyBlock");
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.retainBlock = ^() {
NSLog(@"strongBlock");
};
NSLog(@"self.retainBlock %@", self.retainBlock);
}
结果:
2020-04-20 11:44:11.309201+0800 normalBlock <__NSGlobalBlock__: 0x10b37c038>
2020-04-20 11:44:11.309342+0800 self.copyBlock <__NSGlobalBlock__: 0x10b37c078>
2020-04-20 11:44:11.309445+0800 self.strongBlock <__NSGlobalBlock__: 0x10b37c098>
- 引用了外界局部变量:
- (void)testBlock {
int i = 0;
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d %d", a + b, i);
};
NSLog(@"normalBlock %@", normalBlock);
self.copyBlock = ^() {
NSLog(@"copyBlock %d", i);
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.retainBlock = ^() {
NSLog(@"retainBlock %d", i);
};
NSLog(@"self.retainBlock %@", self.retainBlock);
}
结果:
2020-04-20 11:52:34.301115+0800 normalBlock <__NSStackBlock__: 0x7ffeecec40d8>
2020-04-20 11:52:34.301309+0800 self.copyBlock <__NSMallocBlock__: 0x6000029d5770>
2020-04-20 11:52:34.301398+0800 self.retainBlock <__NSStackBlock__: 0x7ffeecec4088>
此时normalBlock、self.retainBlock变为__NSStackBlock__
,作用域和局部变量一样,出了方法外再调用就很有可能产生崩溃或者其引用的外部变量变为野指针(例子中的i
)。
如果block内引用的为对象属性结果也是相同的:
@property (nonatomic, assign) int i;
- 引用外界全局变量,block就都会变成
__NSGlobalBlock
类型的,例子:
static int i = 0;
- (void)testBlock {
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d %d", a + b, i);
};
NSLog(@"normalBlock %@", normalBlock);
self.copyBlock = ^() {
NSLog(@"copyBlock %d", i);
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.strongBlock = ^() {
NSLog(@"strongBlock %d", i);
};
NSLog(@"self.strongBlock %@", self.strongBlock);
}
结果:
2020-04-20 15:07:07.417933+0800 normalBlock <__NSGlobalBlock__: 0x100002030>
2020-04-20 15:07:07.418412+0800 self.copyBlock <__NSGlobalBlock__: 0x100002070>
2020-04-20 15:07:07.418588+0800 self.strongBlock <__NSGlobalBlock__: 0x100002090>
-
__NSStackBlock
类型的block对其进行copy
可以变成__NSMallocBlock
。例如:
- (void)testBlock {
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d %d", a + b, self.i);
};
NSLog(@"normalBlock %@", normalBlock);
NSLog(@"normalBlock copy %@", [normalBlock copy]);
self.copyBlock = ^() {
NSLog(@"copyBlock %d", self.i);
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.retainBlock = ^() {
NSLog(@"retainBlock %d", self.i);
};
NSLog(@"self.retainBlock %@", self.retainBlock);
NSLog(@"self.retainBlock copy %@", [self.retainBlock copy]);
}
结果:
2020-04-20 14:34:57.815682+0800 normalBlock <__NSStackBlock__: 0x7ffeefbff540>
2020-04-20 14:34:57.816208+0800 normalBlock copy <__NSMallocBlock__: 0x10050c1e0>
2020-04-20 14:34:57.816285+0800 self.copyBlock <__NSMallocBlock__: 0x100502760>
2020-04-20 14:34:57.816379+0800 self.retainBlock <__NSStackBlock__: 0x7ffeefbff4f0>
2020-04-20 14:34:57.816456+0800 self.retainBlock copy <__NSMallocBlock__: 0x100600200>
__NSGlobalBlock
类型的block对其进行copy
依旧为__NSGlobalBlock
。
ARC下
- 对于没有引用外界变量的block(不管用什么修饰的)依旧都为
__NSGlobalBlock
,例子:
@property (nonatomic, copy) void (^copyBlock)(void);
@property (nonatomic, strong) void (^strongBlock)(void);
- (void)testBlock {
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d", a + b);
};
NSLog(@"normalBlock %@", normalBlock);
self.copyBlock = ^() {
NSLog(@"copyBlock");
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.strongBlock = ^() {
NSLog(@"strongBlock");
};
NSLog(@"self.strongBlock %@", self.strongBlock);
}
结果:
2020-04-20 14:58:14.180547+0800 normalBlock <__NSGlobalBlock__: 0x100002040>
2020-04-20 14:58:14.181382+0800 self.copyBlock <__NSGlobalBlock__: 0x100002080>
2020-04-20 14:58:14.181483+0800 self.strongBlock <__NSGlobalBlock__: 0x1000020a0>
- 引用了外界局部变量:
- (void)testBlock {
int i = 0;
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d %d", a + b, i);
};
NSLog(@"normalBlock %@", normalBlock);
// NSLog(@"normalBlock copy %@", [normalBlock copy]);
self.copyBlock = ^() {
NSLog(@"copyBlock %d", i);
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.strongBlock = ^() {
NSLog(@"strongBlock %d", i);
};
NSLog(@"self.strongBlock %@", self.strongBlock);
// NSLog(@"self.strongBlock copy %@", [self.strongBlock copy]);
}
结果:
2020-04-20 15:12:14.453760+0800 normalBlock <__NSMallocBlock__: 0x100545fc0>
2020-04-20 15:12:14.454190+0800 self.copyBlock <__NSMallocBlock__: 0x10066fef0>
2020-04-20 15:12:14.454296+0800 self.strongBlock <__NSMallocBlock__: 0x100670700>
ACR下会将MRC下本为__NSStackBlock__
类型的block copy
成__NSMallocBlock
类型。
如果block内引用的为对象属性结果也是相同的:
@property (nonatomic, assign) int i;
- 引用外界全局变量,block就都会变成
__NSGlobalBlock
类型的,例子:
static int i = 0;
- (void)testBlock {
void(^normalBlock)(int, int) = ^(int a, int b) {
NSLog(@"normalBlock = %d %d", a + b, i);
};
NSLog(@"normalBlock %@", normalBlock);
self.copyBlock = ^() {
NSLog(@"copyBlock %d", i);
};
NSLog(@"self.copyBlock %@", self.copyBlock);
self.strongBlock = ^() {
NSLog(@"strongBlock %d", i);
};
NSLog(@"self.strongBlock %@", self.strongBlock);
}
结果:
2020-04-20 15:18:01.315760+0800 normalBlock <__NSGlobalBlock__: 0x100002040>
2020-04-20 15:18:01.316288+0800 self.copyBlock <__NSGlobalBlock__: 0x100002080>
2020-04-20 15:18:01.316351+0800 self.strongBlock <__NSGlobalBlock__: 0x1000020a0>
小注意事项
- (void)testBlock {
int x = 0;
void(^normalBlock)(void) = ^{
NSLog(@"%d", x);
};
NSLog(@"normalBlock: %@", normalBlock);
NSLog(@"partReferencedBlock: %@", ^{
NSLog(@"%d", x);
});
static int i = 0;
NSLog(@"staticReferencedBlock: %@", ^{
NSLog(@"%d", i);
});
NSLog(@"nothingReferencedBlock: %@", ^{
NSLog(@"nothingReferencedBlock");
});
}
MRC下
2020-04-20 16:55:45.363625+0800 normalBlock: <__NSStackBlock__: 0x7ffeefbff538>
2020-04-20 16:55:45.364103+0800 partReferencedBlock: <__NSStackBlock__: 0x7ffeefbff510>
2020-04-20 16:55:45.364178+0800 staticReferencedBlock: <__NSGlobalBlock__: 0x100002058>
2020-04-20 16:55:45.364258+0800 nothingReferencedBlock: <__NSGlobalBlock__: 0x100002078>
ARC下
2020-04-20 16:53:13.033638+0800 normalBlock: <__NSMallocBlock__: 0x1006a8140>
2020-04-20 16:53:13.034048+0800 partReferencedBlock: <__NSStackBlock__: 0x7ffeefbff510>
2020-04-20 16:53:13.034188+0800 staticReferencedBlock: <__NSGlobalBlock__: 0x100002068>
2020-04-20 16:53:13.034268+0800 nothingReferencedBlock: <__NSGlobalBlock__: 0x100002078>
直接打印partReferencedBlock都为__NSStackBlock__
类型。
ARC下normalBlock、partReferencedBlock 的实现是相同的,为什么一个在堆区,一个在栈区?
这个现象叫做运算符重载。定义 normalBlock 的时候 = 实际上执行了一次 copy,为了管理 normalBlock 的内存,它被转移到了堆区。
只要block内部引用了全局变量(或者啥都没引用),就一定为__NSGlobalBlock__
类型。
结论:
MRC 下,在定义 block 属性时,使用 copy 是为了把 block 从栈区拷贝到堆区,因为栈区中的变量出了作用域之后就会被销毁,无法在全局使用,而把栈区的属性拷贝到堆区中全局共享,就不会被销毁了。
ARC 下,不需要特别使用 copy 修饰,因为 strong 下的 block 属性也就在堆区。
网友评论