playbook/docs/tsl/syntax/31_complex_and_weakref.md

8.1 KiB
Raw Permalink Blame History

Complex And WeakRef

文档类型:语法主线 是否可直接用于生成代码:仅部分 是否含已验证可执行示例:是 是否含已验证反例:是 遇到不确定时跳转到:09_objects_and_classes.md32_object_overloads_and_iteration.md12_pitfalls.md

手册位置:第 31 篇,共 32 篇。上一篇:30_runtime_services_and_global_cache.md。下一篇:32_object_overloads_and_iteration.md

这一篇只讲当前解释器下已经实际跑通的两组新一代语言能力:复数,以及弱引用 / 自动弱引用里当前真正可靠的最小写法。

这一篇解决什么问题

回答“复数现在到底怎样写,弱引用到底有哪些语法真的能用,哪些历史资料里的写法今天不能直接照抄”。

必须记住的规则

  • 复数字面量可以直接写成 a + bj,也可以用 complex(a, b) 构造。
  • datatype(z) 对复数返回 41ifcomplex(z) 对复数返回 1
  • realimagconjabs 当前都已验证可用。
  • 当前解释器里,实数 x 与复数 x + 0j 的相等比较结果为真。
  • complex(array(...), imag) 会返回 Arraycomplex(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

已验证语法

复数常量、类型与基础读取

代码块身份:已验证可执行示例

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

共轭、模与等值比较

代码块身份:已验证可执行示例

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

代码块身份:已验证可执行示例

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)datatype5
  • 上述复数数组长度是 3,三个元素依次是 1+5.5j2+5.5j3+5.5j
  • complex(fmarray[1, 2, 3], 5.5)datatype27
  • 上述复数 FMArray 的单元格类型 datatype(f, 1)41
  • FMArray 长度是 3,三个元素依次是 1+5.5j2+5.5j3+5.5j

弱引用能力的条件编译判定

代码块身份:已验证可执行示例

program test;
begin
{$IFDEF weakptr}
    WriteLn('weakptr');
{$ELSE}
    WriteLn('Noweakptr');
{$ENDIF}
{$IFDEF AutoWeak}
    WriteLn('AutoWeak');
{$ELSE}
    WriteLn('NoAutoWeak');
{$ENDIF}
end.

已验证运行结果:

  • 当前解释器会输出 weakptr
  • 当前解释器也会输出 AutoWeak
  • 说明这代解释器里,弱引用能力的条件编译开关应写成 weakptr,自动弱引用能力的开关应写成 AutoWeak

weakrefweakref_getcheckweakref

对象仍然存活时:

代码块身份:已验证可执行示例

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,只把主体改成下面这样:

代码块身份:配置片段 / 概念骨架

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

当前还额外验证到:

代码块身份:反例 / 不可照写

a := new TNode('A');
w := weakref(a);
a := nil;
t := weakref_get(w);

上面这种写法在当前解释器里会运行报错,因此访问弱引用前应先做 checkweakref(w) 判定。

[WeakRef][AutoRef] 成员标记

成员级标记当前可以直接写在类里:

代码块身份:已验证可执行示例

program test;
type TChild = class
public
    [WeakRef] owner1;
    [AutoRef] owner2;
end;
begin
    WriteLn(1);
end.

已验证运行结果:

  • 上述写法可以正常执行,并输出 1
  • 说明 [WeakRef] field;[AutoRef] field; 当前都能通过

[WeakRef] 成员不会阻止对象析构:

代码块身份:已验证可执行示例

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.

已验证运行结果:

  • 输出顺序是 DestroyAfterNil
  • 说明 TB.owner 作为 [WeakRef] 成员,不会把 TA 实例继续强持有

暂不在本页展开的部分

  • 复数统计函数、分解函数与更大函数族
  • MakeWeakRefMakeStrongRefweakref_check 等别名接口
  • property write 传播自动弱引用的更复杂规则
  • 循环引用场景里的完整模式化写法

最小可编译示例

复数最短骨架:

代码块身份:已验证可执行示例

program test;
begin
    z := 4 + 3j;
    WriteLn(real(z));
    WriteLn(imag(z));
end.

弱引用最短骨架:

代码块身份:已验证可执行示例

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] 成员会继续强持有对象。

代码块身份:反例 / 不可照写

type TChild = class
public
    owner0;
    [WeakRef] owner1;
    WeakRef;
    owner2;
    AutoRef;
    owner3;
end;

上面这种把 WeakRef; / AutoRef; 当作类内段落切换的写法,在当前解释器里会编译失败,错误信息包含 invalid class definition

跳转指引