弱引用
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
所引用对象的引用/所有权。