Skip to content

Commit

Permalink
[Change] update article
Browse files Browse the repository at this point in the history
  • Loading branch information
ethanguan26 committed Jul 26, 2020
1 parent 7211ce4 commit de11eaf
Showing 1 changed file with 43 additions and 3 deletions.
46 changes: 43 additions & 3 deletions _posts/2020-04-07-Block.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,10 +101,14 @@ self.aProperty = @"something";

`descriptor`是一个指向结构体的指针,声明了`block`对象的整体大小以及用于复制和处理助手的函数指针。这些助手往往在`block`捕获对象时进行持有、释放操作时起到作用。

一个`block`包含所有的被截获的对象的副本。这些副本存储在`descriptor`之后,并占用存储所有捕获变量所需的空间。注意,这并不意味着对象本身被复制,并且还包含变量的指针。运行`block`时,将从该内存区域读取捕获的变量,这就是为什么需要将该`block`作为参数传递到`invoke`函数的原因。

### 全局、栈和堆中的Blocks

`block`的三种分类:

* GlobalBlock,没有访问auto变量。
* StackBlock,访问auto变量。
* MallocBlock,对StackBlock执行了copy操作

当一个`block`被定义后,它将在栈中占有一块空间。也就是说这个`block`仅仅在所定义的作用域才有效。代码这么写会有问题:

```objectivec
Expand Down Expand Up @@ -147,7 +151,13 @@ void (^block)() = ^{
};
```

### block的copy

ARC下,编译器会根据情况自动将栈上的`block`进行`copy`操作复制到堆上:

1. block作为函数返回值时
2. 将block赋值给__strong 指针时
3. block作为CocoaAPI中方法名含有usingBlock的方式参数时

## 使用typedefs

Expand Down Expand Up @@ -357,4 +367,34 @@ myBlock();
![Block6](../../../assets/images/block6.png){:.shadow}
可以看出,无论是基础类型还是OC对象,在block截获后,加入`__block`后,OC会将该变量声明成`__Block_bref_变量名_0`这样的结构体,然后将地址当作参数,因此在`block`内部,可以通过`__forwarding`获取到该变量堆上的地址,从而修改或者重新分配空间。我们也可以通过LLVM中的p指令来打印截获变量的地址,发现如果不加修饰符的话,地址是不一样的。
可以看出,无论是基础类型还是OC对象,在block截获后,加入`__block`后,OC会将该变量声明成`__Block_bref_变量名_0`这样的结构体,然后将其地址当作参数,因此在`block`内部,可以通过`__forwarding`获取到该变量堆上的地址,从而修改或者重新分配空间。我们也可以通过LLVM中的p指令来打印截获变量的地址,发现如果不加修饰符的话,地址是不一样的。
### static变量截获
```objectivec
static int age = 20;
void (^block)(void) = ^{
NSLog(@"age = %d", age);
}
struct __main_block_impl_0 {
struct __block_imp impl;
struct __main_block_desc_0 *Desc;
int *age; //外部截获的变量
}
```

block初始化时,将age的地址传入block,在调用block时,会访问age的地址,因此age的改动会影响block内的值。

### __block修饰的对象类型

-__block变量在栈上时,不会对指向的对象产生强引用
-__block变量被copy到堆时
- 会调用__block变量内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会根据所指向对象的修饰符(__ strong、__ weak、__unsafe_unretained)做出相应的操作,形成强引用(retain)或者弱引用(注意:这里仅限于ARC时会retain,MRC时不会retain)

- 如果__block变量从堆上移除
- 会调用__block变量内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放指向的对象(release)

0 comments on commit de11eaf

Please sign in to comment.