Vala编程语言高级特性-弱引用和所有权

弱引用

Vala 的内存管理基于自动引用计数。每次将对象赋值给变量时,其内部引用计数会增加 1;每次引用对象的变量超出作用域时,其内部引用计数会减少 1。如果引用计数达到 0,对象将被释放。

然而,在数据结构中可能会形成引用循环。例如,在树形数据结构中,子节点持有对其父节点的引用,而父节点也持有对子节点的引用;或者在双向链表中,每个元素持有对其前驱节点的引用,而前驱节点也持有对其后继节点的引用。

在这些情况下,即使对象应该被释放,它们也可能仅仅通过相互引用而保持存活状态。为了打破这种引用循环,您可以在其中一个引用上使用 weak 修饰符:

cs 复制代码
class Node {
    public weak Node prev;
    public Node next;
}

这个主题在以下页面有详细解释:内存管理。

所有权

无主引用

通常在 Vala 中创建对象时,您会获得一个指向它的引用。具体来说,这意味着除了传递指向内存中对象的指针外,还会在对象本身中记录该指针的存在。同样,每当创建对象的另一个引用时,也会记录该引用。由于对象知道有多少个引用指向它,因此它可以在需要时自动被移除。这是内存管理的基础。

方法所有权

相反,无主引用不会记录在它们所引用的对象中。这允许对象在逻辑上应该被移除时被移除,而不管可能仍然存在对它的引用这一事实。实现这一点的常用方法是定义一个返回无主引用的方法,例如:

cs 复制代码
class Test {
    private Object o;

    public unowned Object get_unowned_ref() {
        this.o = new Object();
        return this.o;
    }
}

当调用此方法时,为了收集对返回对象的引用,您必须期望收到一个弱引用:

复制代码
unowned Object o = get_unowned_ref();

这个看似过于复杂的例子的原因在于所有权的概念。

如果对象"o"没有存储在类中,那么当方法"get_unowned_ref"返回时,"o"将变为无主的(即没有对它的引用)。如果是这种情况,对象将被删除,并且该方法永远不会返回有效的引用。

如果返回值没有被定义为无主的,所有权将传递给调用代码。然而,调用代码期望的是一个无主引用,它无法接收所有权。

如果调用代码写成:

复制代码
Object o = get_unowned_ref();

Vala 将尝试获取无主引用所指向实例的引用或副本。

属性所有权

与普通方法相反,属性总是具有无主返回值。这意味着您不能返回在 get 方法内部创建的新对象。这也意味着您不能使用方法调用的有主返回值。这个有些令人困惑的事实是因为属性值由拥有该属性的对象所有。获取该属性值的调用不应从对象端窃取或复制(通过重复或增加引用计数)该值。

因此,以下示例将导致编译错误:

cs 复制代码
public Object property {
    get {
        return new Object();   // 错误:属性返回一个无主引用,
                               // 新创建的对象将在 getter 作用域结束时被删除,
                               // getter 的调用者最终会收到一个指向已删除对象的无效引用。
    }
}

您也不能这样做:

cs 复制代码
public string property {
    get {
        return getter_method();   // 错误:原因同上。
    }
}

public string getter_method() {
    return "some text"; // "some text" 在此处被复制并返回。
}

另一方面,这是完全可以的:

cs 复制代码
public string property {
    get {
        return getter_method();   // 正确:getter_method 返回一个无主值
    }
}

public unowned string getter_method() {
    return "some text";
    // 不要担心文本没有赋值给任何强变量。
    // Vala 中的字面字符串始终由程序模块本身所有,
    // 并且只要模块在内存中,它们就存在。
}

unowned 修饰符可用于使自动属性的存储变为无主的。这意味着:

复制代码
public unowned Object property { get; private set; }

等同于:

cs 复制代码
private unowned Object _property;

public Object property {
    get { return _property; }
}

关键字 owned 可用于特别要求属性返回值的拥有所有权引用,从而导致属性值在对象端被复制。在添加 owned 关键字之前请三思。它是一个属性还是仅仅是一个 get_xxx 方法?您的设计中可能也存在问题。无论如何,以下代码是一个正确的片段:

复制代码
public owned Object property { owned get { return new Object(); } }

无主引用与后面描述的指针起着类似的作用。然而,它们比指针更易于使用,因为它们可以轻松转换为普通引用。但是,除非您知道自己在做什么,否则通常不应在程序中广泛使用它们。

所有权转移

关键字 owned 用于转移所有权。

作为参数类型的前缀,它意味着对象的所有权被转移到此代码上下文中。

作为类型转换运算符,它可用于避免复制非引用计数类,这通常在 Vala 中是不可能的。例如:

复制代码
Foo foo = (owned) bar;

这意味着 bar 将被设置为 null,而 foo 继承 bar 所引用对象的引用/所有权。

相关推荐
一只学java的小汉堡2 小时前
Spring Boot 配置详解:从引导器到注解实战(初学者指南)
java·spring boot·后端
IT_陈寒2 小时前
Python开发者必坑指南:3个看似聪明实则致命的‘优化’让我损失了50%性能
前端·人工智能·后端
悠哉悠哉愿意2 小时前
【ROS2学习笔记】服务
开发语言·笔记·学习·ros2
Ivanqhz3 小时前
Rust的错误处理
开发语言·后端·rust
easyboot3 小时前
python的print加入颜色显示
开发语言·python
alwaysrun3 小时前
Rust与C接口交互
c语言·rust·交互
say_fall4 小时前
精通C语言(1.内存函数)
c语言·开发语言
草莓熊Lotso4 小时前
《吃透 C++ vector:从基础使用到核心接口实战指南》
开发语言·c++·算法
java1234_小锋5 小时前
[免费]基于Python的Flask+Vue进销存仓库管理系统【论文+源码+SQL脚本】
后端·python·flask