mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-24 20:00:37 +00:00
update: content update
This commit is contained in:
@@ -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` 会被赋值给结构体内的 age,age 为值传递;`height(_height)` 写法,表明传入的 `_height` 会被复制给结构体内的 height,height 为引用传递
|
||||
- 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 外定义的 NSMutableArray,block 内只是使用数组则不需要` __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。
|
||||
|
||||
|
||||
|
||||
## 总结
|
||||
|
||||
Reference in New Issue
Block a user