329 lines
8.1 KiB
Markdown
329 lines
8.1 KiB
Markdown
# Complex And WeakRef
|
||
|
||
文档类型:语法主线
|
||
是否可直接用于生成代码:仅部分
|
||
是否含已验证可执行示例:是
|
||
是否含已验证反例:是
|
||
遇到不确定时跳转到:[09_objects_and_classes.md](09_objects_and_classes.md)、[32_object_overloads_and_iteration.md](32_object_overloads_and_iteration.md)、[12_pitfalls.md](12_pitfalls.md)
|
||
|
||
手册位置:第 31 篇,共 32 篇。上一篇:[30_runtime_services_and_global_cache.md](30_runtime_services_and_global_cache.md)。下一篇:[32_object_overloads_and_iteration.md](32_object_overloads_and_iteration.md)。
|
||
|
||
这一篇只讲当前解释器下已经实际跑通的两组新一代语言能力:复数,以及弱引用 / 自动弱引用里当前真正可靠的最小写法。
|
||
|
||
## 这一篇解决什么问题
|
||
|
||
回答“复数现在到底怎样写,弱引用到底有哪些语法真的能用,哪些历史资料里的写法今天不能直接照抄”。
|
||
|
||
## 必须记住的规则
|
||
|
||
- 复数字面量可以直接写成 `a + bj`,也可以用 `complex(a, b)` 构造。
|
||
- `datatype(z)` 对复数返回 `41`,`ifcomplex(z)` 对复数返回 `1`。
|
||
- `real`、`imag`、`conj`、`abs` 当前都已验证可用。
|
||
- 当前解释器里,实数 `x` 与复数 `x + 0j` 的相等比较结果为真。
|
||
- `complex(array(...), imag)` 会返回 `Array`;`complex(fmarray..., imag)` 会返回 `FMArray`,并且其单元格类型是 `41`。
|
||
- 弱引用能力的条件编译宏当前已验证是 `weakptr`;自动弱引用相关宏是 `AutoWeak`。
|
||
- `weakref(obj)` 可以创建弱引用;对象仍存活时,`weakref_get(w)` 可以拿回强引用。
|
||
- `checkweakref(w)` 当前已验证:对象仍存活时返回 `1`,对象已释放后返回 `-1`。
|
||
- 对已经失效的弱引用直接调用 `weakref_get(w)`,当前解释器里会运行报错,不要把它当成“安全返回 nil”的接口。
|
||
- 类成员上的 `[WeakRef] field;` 和 `[AutoRef] field;` 当前已验证可以通过。
|
||
- `[WeakRef]` 成员不会阻止对象析构;强引用释放后,对象会正常销毁。
|
||
- 历史资料里的类内段落式 `WeakRef;` / `AutoRef;` 在当前解释器里没有通过,错误信息包含 `invalid class definition`。
|
||
|
||
## 已验证语法
|
||
|
||
### 复数常量、类型与基础读取
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
z1 := 4 + 3j;
|
||
z2 := complex(5, -2);
|
||
WriteLn(datatype(z1));
|
||
WriteLn(ifcomplex(z1));
|
||
WriteLn(real(z1));
|
||
WriteLn(imag(z1));
|
||
WriteLn(z2);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `datatype(4 + 3j)` 返回 `41`
|
||
- `ifcomplex(4 + 3j)` 返回 `1`
|
||
- `real(4 + 3j)` 返回 `4`
|
||
- `imag(4 + 3j)` 返回 `3`
|
||
- `complex(5, -2)` 打印结果是 `5-2j`
|
||
|
||
### 共轭、模与等值比较
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
z := 4 + 3j;
|
||
c := conj(z);
|
||
WriteLn(abs(z));
|
||
WriteLn(real(c));
|
||
WriteLn(imag(c));
|
||
if 3.15 = 3.15 + 0j then
|
||
WriteLn(1);
|
||
else
|
||
WriteLn(0);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `abs(4 + 3j)` 返回 `5`
|
||
- `conj(4 + 3j)` 的实部是 `4`、虚部是 `-3`
|
||
- `3.15 = 3.15 + 0j` 当前比较结果为真
|
||
|
||
### 复数 `Array` 与复数 `FMArray`
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
a := complex(array(1, 2, 3), 5.5);
|
||
f := complex(fmarray[1, 2, 3], 5.5);
|
||
WriteLn(datatype(a));
|
||
WriteLn(length(a));
|
||
WriteLn(a[0], ',', a[1], ',', a[2]);
|
||
WriteLn(datatype(f));
|
||
WriteLn(datatype(f, 1));
|
||
WriteLn(length(f));
|
||
WriteLn(f[0], ',', f[1], ',', f[2]);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `complex(array(1, 2, 3), 5.5)` 的 `datatype` 是 `5`
|
||
- 上述复数数组长度是 `3`,三个元素依次是 `1+5.5j`、`2+5.5j`、`3+5.5j`
|
||
- `complex(fmarray[1, 2, 3], 5.5)` 的 `datatype` 是 `27`
|
||
- 上述复数 `FMArray` 的单元格类型 `datatype(f, 1)` 是 `41`
|
||
- 该 `FMArray` 长度是 `3`,三个元素依次是 `1+5.5j`、`2+5.5j`、`3+5.5j`
|
||
|
||
### 弱引用能力的条件编译判定
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
{$IFDEF weakptr}
|
||
WriteLn('weakptr');
|
||
{$ELSE}
|
||
WriteLn('Noweakptr');
|
||
{$ENDIF}
|
||
{$IFDEF AutoWeak}
|
||
WriteLn('AutoWeak');
|
||
{$ELSE}
|
||
WriteLn('NoAutoWeak');
|
||
{$ENDIF}
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 当前解释器会输出 `weakptr`
|
||
- 当前解释器也会输出 `AutoWeak`
|
||
- 说明这代解释器里,弱引用能力的条件编译开关应写成 `weakptr`,自动弱引用能力的开关应写成 `AutoWeak`
|
||
|
||
### `weakref`、`weakref_get` 与 `checkweakref`
|
||
|
||
对象仍然存活时:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
type TNode = class
|
||
name;
|
||
function create(v);
|
||
begin
|
||
name := v;
|
||
end;
|
||
end;
|
||
begin
|
||
a := new TNode('A');
|
||
w := weakref(a);
|
||
WriteLn(checkweakref(w));
|
||
s := weakref_get(w);
|
||
WriteLn(s.name);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- `checkweakref(w)` 返回 `1`
|
||
- `weakref_get(w)` 能拿回强引用
|
||
- 通过拿回的强引用可继续访问对象成员,此处输出 `A`
|
||
|
||
沿用同一个 `TNode`,只把主体改成下面这样:
|
||
|
||
代码块身份:配置片段 / 概念骨架
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
a := new TNode('A');
|
||
w := weakref(a);
|
||
WriteLn(checkweakref(w));
|
||
a := nil;
|
||
WriteLn(checkweakref(w));
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 创建后 `checkweakref(w)` 返回 `1`
|
||
- 把最后一个强引用设为 `nil` 后,`checkweakref(w)` 返回 `-1`
|
||
|
||
当前还额外验证到:
|
||
|
||
代码块身份:反例 / 不可照写
|
||
|
||
```text
|
||
a := new TNode('A');
|
||
w := weakref(a);
|
||
a := nil;
|
||
t := weakref_get(w);
|
||
```
|
||
|
||
上面这种写法在当前解释器里会运行报错,因此访问弱引用前应先做 `checkweakref(w)` 判定。
|
||
|
||
### `[WeakRef]` 与 `[AutoRef]` 成员标记
|
||
|
||
成员级标记当前可以直接写在类里:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
type TChild = class
|
||
public
|
||
[WeakRef] owner1;
|
||
[AutoRef] owner2;
|
||
end;
|
||
begin
|
||
WriteLn(1);
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 上述写法可以正常执行,并输出 `1`
|
||
- 说明 `[WeakRef] field;` 和 `[AutoRef] field;` 当前都能通过
|
||
|
||
`[WeakRef]` 成员不会阻止对象析构:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
type TA = class
|
||
public
|
||
function destroy();
|
||
begin
|
||
WriteLn('Destroy');
|
||
end;
|
||
end;
|
||
|
||
type TB = class
|
||
public
|
||
[WeakRef] owner;
|
||
function Bind(v);
|
||
begin
|
||
owner := v;
|
||
end;
|
||
end;
|
||
begin
|
||
a := new TA();
|
||
b := new TB();
|
||
b.Bind(a);
|
||
a := nil;
|
||
WriteLn('AfterNil');
|
||
end.
|
||
```
|
||
|
||
已验证运行结果:
|
||
|
||
- 输出顺序是 `Destroy`、`AfterNil`
|
||
- 说明 `TB.owner` 作为 `[WeakRef]` 成员,不会把 `TA` 实例继续强持有
|
||
|
||
## 暂不在本页展开的部分
|
||
|
||
- 复数统计函数、分解函数与更大函数族
|
||
- `MakeWeakRef`、`MakeStrongRef`、`weakref_check` 等别名接口
|
||
- `property write` 传播自动弱引用的更复杂规则
|
||
- 循环引用场景里的完整模式化写法
|
||
|
||
## 最小可编译示例
|
||
|
||
复数最短骨架:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
begin
|
||
z := 4 + 3j;
|
||
WriteLn(real(z));
|
||
WriteLn(imag(z));
|
||
end.
|
||
```
|
||
|
||
弱引用最短骨架:
|
||
|
||
代码块身份:已验证可执行示例
|
||
|
||
```tsl
|
||
program test;
|
||
type TNode = class
|
||
name;
|
||
function create(v);
|
||
begin
|
||
name := v;
|
||
end;
|
||
end;
|
||
begin
|
||
a := new TNode('A');
|
||
w := weakref(a);
|
||
WriteLn(checkweakref(w));
|
||
end.
|
||
```
|
||
|
||
## 常见误写
|
||
|
||
- 把弱引用能力的条件编译宏写成 `WeakRef`。
|
||
- 以为 `weakref_get(deadWeakRef)` 会像普通可空访问那样安全返回 `nil`。
|
||
- 以为历史资料里的类内段落式 `WeakRef;` / `AutoRef;` 现在也能直接通过。
|
||
- 以为 `[WeakRef]` 成员会继续强持有对象。
|
||
|
||
代码块身份:反例 / 不可照写
|
||
|
||
```text
|
||
type TChild = class
|
||
public
|
||
owner0;
|
||
[WeakRef] owner1;
|
||
WeakRef;
|
||
owner2;
|
||
AutoRef;
|
||
owner3;
|
||
end;
|
||
```
|
||
|
||
上面这种把 `WeakRef;` / `AutoRef;` 当作类内段落切换的写法,在当前解释器里会编译失败,错误信息包含 `invalid class definition`。
|
||
|
||
## 跳转指引
|
||
|
||
- 回看语法主入口:见 [index.md](index.md)
|
||
- 回看对象主线:见 [09_objects_and_classes.md](09_objects_and_classes.md)
|
||
- 看对象重载:见 [32_object_overloads_and_iteration.md](32_object_overloads_and_iteration.md)
|