update: content update

This commit is contained in:
Unix_Kernel
2024-11-13 14:47:32 +08:00
parent dae10db9d4
commit aca020701b
14 changed files with 285 additions and 78 deletions

View File

@@ -4,7 +4,7 @@
>
> - block 原理是什么,系统是如何实现的?
> - __block 的作用是什么?
> - block 作为属性时,为什么用 copu 修饰?
> - block 作为属性时,为什么用 copy 修饰?
> - block 在修改 NSMutableArray 的时候,需要加 __block 吗?
>
> 带着问题探究本文。
@@ -19,11 +19,11 @@ Demo
```objective-c
NSInteger age = 27;
void(^block)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
NSLog(@"age is %zd", age);
NSLog(@"a is %zd, b is %zd", a, b);
};
block(1, 2);
void(^block)(NSInteger, NSInteger) = ^(NSInteger a, NSInteger b) {
NSLog(@"age is %zd", age);
NSLog(@"a is %zd, b is %zd", a, b);
};
block(1, 2);
```
用指令`xcrun --sdk iphoneos clang -arch arm64 ViewController.m -rewrite-objc -o ViewController-arm64.cpp` 转为 c++
@@ -224,6 +224,8 @@ struct __ViewController__viewDidLoad_block_desc_0 {
### auto 变量捕获
> 在 C 和 Objective-C 编程语言中,`auto` 关键字用于声明自动存储期的变量。自动存储期的变量会在定义它们的块block或作用域scope中自动创建并在退出该作用域时自动销毁。这是变量存储期的默认行为因此 `auto` 关键字实际上是可选的,但有时候为了清晰起见,开发者可能会显式使用它。
Demo1
一个最简单的 block参数和返回值都是 void内部仅一条打印语句。
@@ -248,11 +250,13 @@ printBlock();
Demo2: 捕获外部变量
```objective-c
age = 27;
void(^printAgeBlock)(void) = ^ {
NSLog(@"age is %zd", age);
};
age = 28;
printAgeBlock();
// 27
```
用指令 `xcrun --sdk iphoneos clang -arch arm64 ViewController.m -rewrite-objc -o ViewController-arm64.cpp` 转换为 c++ 代码
@@ -289,6 +293,7 @@ void(^printInfoBlock)(void) = ^ {
age = 28;
height = 176;
printInfoBlock();
// age is 27, height is 176
```
用指令 `xcrun --sdk iphoneos clang -arch arm64 ViewController.m -rewrite-objc -o ViewController-arm64.cpp` 转换为 c++ 代码
@@ -301,7 +306,7 @@ printInfoBlock();
- 可以看到我们编写的 block 被声明为一个 `__ViewController__viewDidLoad_block_impl_0` 类型的结构体
- 结构体内有个构造函数见50774行代码。
- c++ 中,构造方法中 `age(_age) `的写法,表明传入的 `_age` 会被赋值给结构体内的 ageage 为值传递;`height(_height)` 写法,表明传入的 `_height` 会被复制给结构体内的 heightheight 为引用传递
- c++ 中,构造方法中 `age(_age) `的写法,表明传入的 `_age` 会被赋值给结构体内的 age`NSInteger _age` 则 age 为值传递;`height(_height)` 写法,表明传入的 `_height` 会被复制给结构体内的 height`NSInteger *_height` 则 height 为引用传递
- 50797行代码调用结构体的构造方法age 以值传递的方式传入参数,结构体构造方法内部将 参数 age 的值保存到结构体内部的 age 中。height 以引用传递的方式传入参数,结构体构造方法内,将参数 height 的引用保存起来
- 因为 age 是值传递。所以即使在 50798 行代码对 age 进行了修改,结构体内部的 age 值不变
- 因为 height 是引用传递。所以在 50799 行代码对 height 进行了修改,结构体内部的 height 值跟着改变
@@ -352,6 +357,12 @@ QA为什么局部变量存在捕获全局变量不需要捕获
全局变量到哪都可以访问,所以没必要捕获。局部变量因为作用域的问题,所以需要捕获到哪步,以便后续使用。
理解 block 的本质和意义:
- block 本质上就是一个 oc 对象,也有 isa 指针
- block 是封装了函数调用和函数调用环境的 OC 对象
### 变量捕获总结
@@ -752,6 +763,46 @@ Demo4
block 嵌套。多个 block 存在先后关系时
- 看看最晚的一个 block 是什么修饰的。如果是 strong早期的是 weak则也不会释放。
- 看看最晚的一个 block 是什么修饰的。如果是 weak早起是 strong则第一个 block 内部的可以正常访问,之后调用对象的 dealloc 方法,最后的 block 访问因为对象释放了,所以访问为 null
```objective-c
Person *p = [[Person alloc] init];
p.name = @"杭城小刘";
__weak Person *weakPerson = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakPerson.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", p.name);
});
});
NSLog(@"-touchesBegan:withEvent:");
2024-08-13 20:58:46.500553+0800 BlockExplore[29848:967516] -touchesBegan:withEvent:
2024-08-13 20:58:47.549486+0800 BlockExplore[29848:967516] 杭城小刘
2024-08-13 20:58:49.550015+0800 BlockExplore[29848:967516] 杭城小刘
2024-08-13 20:58:49.550315+0800 BlockExplore[29848:967516] -[Person dealloc]
Person *p = [[Person alloc] init];
p.name = @"杭城小刘";
__weak Person *weakPerson = p;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", p.name);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@", weakPerson.name);
});
});
NSLog(@"-touchesBegan:withEvent:");
2024-08-13 20:59:51.265796+0800 BlockExplore[29889:968688] -touchesBegan:withEvent:
2024-08-13 20:59:52.313063+0800 BlockExplore[29889:968688] 杭城小刘
2024-08-13 20:59:52.313265+0800 BlockExplore[29889:968688] -[Person dealloc]
2024-08-13 20:59:54.313367+0800 BlockExplore[29889:968688] (null)
```
### block 如何修改变量
#### __block 修饰基本数据类型
@@ -949,7 +1000,7 @@ __attribute__((__blocks__(byref))) __Block_byref_num2_0 num2 = {(void*)0,(__Bloc
- block 外定义的 NSMutableArrayblock 内只是使用数组则不需要` __block`
- 如果在 block 操作指针,则需要加 `__block`
- 如果在 block 操作指针,则需要加 `__block`
注意:`__weak` 只可以用来修饰对象,(终端用 clang 处理)否则 clang 会报错 `warning: 'objc_ownership' only applies to Objective-C object or block pointer types; type here is 'int' [-Wignored-attributes]`
@@ -1160,7 +1211,7 @@ in block: age = 28, address is 0x600000464938
**一言以蔽之,`__forwarding` 指针是为了在 `__block` 变量从栈复制到堆上后,在 block 外对 `__block` 变量的修改也可以同步到堆上实际存储的 `__block` 变量的结构体上。也就是抹平栈、堆上对变量操作的差异。**
不论在
## Block 内存引用
@@ -1221,10 +1272,9 @@ __Block_byref_p_0 *__forwarding; 8
int __size; 4
void (*__Block_byref_id_object_copy)(void*, void*); 8
void (*__Block_byref_id_object_dispose)(void*); 8
Person *p;
Person *p; 8
};
__attribute__((__blocks__(byref))) __Block_byref_p_0 p = {
0,
&p,
@@ -1316,6 +1366,14 @@ p.block();
## 为什么加 weakself、strongself
weakSelf 是为了使 block 不持有 self避免 Retain Circle 循环引用。在 Block 内如果需要访问 self 的方法、变量,建议使用 weakSelf。
strongSelf 的目的是因为一旦进入 block 执行,假设不允许 self 在这个执行过程中释放,就需要加入 strongSelf。
block 执行完后这个 strongSelf 会自动释放,没有不会存在循环引用问题。如果在 Block 内需要多次 访问 self则需要使用 strongSelf。
## 总结