弱引用

在電腦程式設計中，弱引用與「強引用」相對，是指不能確保其引用的對象不會被垃圾回收器回收的引用。一個對象若只被弱引用所引用，則被認為是不可訪問（或弱可訪問）的，並因此可能在任何時刻被回收。一些配有垃圾回收機制的語言，如Java、C#、Python、Perl、Lisp等都在不同程度上支持弱引用。

## 垃圾回收

垃圾回收用來清理不會再使用的對象，從而降低記憶體洩露和數據損壞的可能性。垃圾回收主要有兩種類型：追蹤和引用計數。引用計數會記錄給定對象的引用個數，並在引用個數為零時收集該對象。由於一次僅能有一個對象被回收，引用計數無法回收循環引用的對象。一組相互引用的對象若沒有被其它對象直接引用，並且不可訪問，則會永久存活下來。一個應用程式如果持續地產生這種不可訪問的對象群組，就會發生記憶體洩漏。在對象群組內部使用弱引用（即不會在引用計數中被計數的引用）有時能避免出現引用環，因此弱引用可用於解決循環引用的問題。如Apple的Cocoa框架就推薦使用這種方法，具體為，在父對子引用時使用強引用，子對父引用時使用弱引用，從而避免了循環引用。

程序對一些對象只進行弱引用，通過此法可以指明哪些對象是不重要的，因此弱引用也用於儘量減少內存中不必要的對象存在的數量。

## 變種

有些語言包含多種強度的弱引用。例如Java，在java.lang.ref包中定義了軟引用、弱引用和虛引用，引用強度依次遞減。每種引用都有相對應的可訪問性概念。垃圾回收器（GC）通過判斷對象的可訪問性類型來確定何時回收該對象。當一個對象是軟可訪問的，垃圾回收器就可以安全回收這個對象，但如果垃圾回收器認為JVM還能空出可用記憶體（比如JVM還有大量未使用的堆空間），則有可能不會立刻回收軟可訪問的對象。但對於弱可訪問的對象，一旦被垃圾回收器注意到，就會被回收。和其他引用種類不同，虛引用無法跟蹤。但另一方面，虛引用提供了一種機制，當一個對象被回收時程序可以得到通知（實現於ReferenceQueues）。

一些未配有垃圾回收機制的語言，比如C++，也提供強/弱引用的功能，以作為對垃圾回收庫的支持。在C++中，普通指針可看做弱引用，智能指針可看做強引用，儘管指針不能算"真正"的弱引用，因為弱引用應該能知道何時對象變成不可訪問的了。

## 示例

弱引用可用於在應用程式中維護一個當前被引用的對象的列表。該列表必須弱引用到那些對象，否則一旦對象被添加到列表中，由於它們被列表引用了，在程序運行期間將永遠不會被回收。

### Java

Java 1.2在1998年介入了兩種弱引用，一個叫做「軟引用」（意圖用於維護由垃圾回收器管理的記憶體內緩存，但是在實踐中並不適合工作於具有動態堆的平台比如Android之上），和另一種簡單的「弱引用」。它還增加了叫做「虛引用」的一種有關的實驗性機制來替代危險而低效的`finalize()`機制。如果創建了一個弱引用，然後在代碼的其它地方用`get()`獲得真實對象，由於弱引用無法阻止垃圾回收，`get()`隨時有可能開始返回 `null`（假如對象沒有被強引用）。

```java
package org.wikipedia.examples;

import java.lang.ref.WeakReference;

public class ReferenceTest {
    public static void main(String[] args) throws InterruptedException {
        WeakReference r = new WeakReference("I'm here");
        String s = "I'm here";
        System.out.printf("Before gc: r=%s, static=%s%n", r.get(), s);
        System.gc(); // assume GC is run
        Thread.sleep(100);

        // 只有r.get()变为了null.
        System.out.printf("After gc: r=%s, static=%s%n", r.get(), s);
    }
}
```

弱引用還可以用來實現緩存。例如用弱哈希表，即通過弱引用來緩存各種引用對象的哈希表。當垃圾回收器運行時，假如應用程式的記憶體占用量高到一定程度，那些不再被其它對象所引用的緩存對象就會被自動釋放。

### Lua

```lua
weak_table = setmetatable({}, {__mode="v"})
weak_table.item = {}
print(weak_table.item)
collectgarbage()
print(weak_table.item)
```

### Objective-C 2.0

在Objective-C 2.0中，除了垃圾回收，自動引用計數也會受弱引用的影響。下面這個例子中的所有變量和屬性都是弱引用。

```objc
@interface WeakRef : NSObject
{
    __weak NSString *str1;
    __assign NSString *str2;
}
 
@property (nonatomic, weak) NSString *str3;
@property (nonatomic, assign) NSString *str4;
 
@end
```

`weak`(`__weak`)和`assign`(`__assign`)的區別在於，當變量指向的對象被重新分配時，變量的值是否會跟著改變。`weak`聲明的變量會變為`nil`，而`assign`聲明的變量則會保持不變，成為一個懸擺指針。從Mac OX 10.7 「獅子」系統和iOS 5開始，隨著Xcode 4.1版本的推出，`weak`引用被引入到Objective-C 語言中（4.2版本開始支持iOS）。老版本的Mac OS X、iOS和GNUstep僅支持用`assign`聲明弱引用。

### Python

```pycon
>>> import weakref
>>> import gc
>>> class Egg:
...     def spam(self):
...         print("I'm alive!")
...
>>> obj = Egg()
>>> weak_obj = weakref.ref(obj)
>>> weak_obj().spam()
I'm alive!
>>> obj = "Something else"
>>> gc.collect()
35
>>> weak_obj().spam()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'NoneType' object has no attribute 'spam'
```

### Smalltalk

```Smalltalk
|a s1 s2|

s1 := 'hello' copy. "这是个强引用"
s2 := 'world' copy. "这是个强引用"
a := WeakArray with:s1 with:s2.
a printOn: Transcript. 
ObjectMemory collectGarbage.
a printOn: Transcript. "两个元素都还在"

s1 := nil. "移除强引用" 
ObjectMemory collectGarbage.
a printOn: Transcript. "第一个元素消失"

s2 := nil. "移除强引用" 
ObjectMemory collectGarbage.
a printOn: Transcript. "第二个元素消失" 
```

### Vala

```vala
class Node {
    public weak Node prev; //弱引用可避免列表中的节点之间出现循环引用
    public Node next;
}
```