# 讓 Mole 的解除安裝列表一點就出來

> 點一個 App 要等一兩秒，時間花在了哪，哪些檢查挪到了背景，以及 ChatGPT 的一萬五千個暫存資料夾為什麼合成了一行。

Published: 2026-10-28

在 Mole 的解除安裝頁裡點一個 App，右邊會先空一下，再出現「正在掃描」，過半秒到一秒殘留才列出來，Xcode 每點一次要等三四秒，ChatGPT 要六秒，展開 ChatGPT 往下捲還會一頓一頓的。我想認真看一下這段時間到底花在了哪，順便也翻了幾個開源解除安裝工具是怎麼做的，這些改動在 10 月 5 日做完，已經隨 Mole 1.16.0 正式版發布，下面是當時的實測和取捨。

## 慢的不是 App 本身

一開始以為 App 越大越慢，測下來並不是，一個 6.6 MB、只有六項殘留的小工具，第一次點開也要 0.56 秒左右。時間主要花在每次點擊都要做的幾件固定的事上，下面是在我的 M5 Pro 上用 Release 版本測的，取三次的中位數：

- 讀取 Homebrew 的 cask 目錄，這份目錄有 5.6 MB，每次大約 200 ms，用來判斷這個 App 是不是用 Homebrew 裝的。
- 校驗 App 的程式碼簽名，而且校驗了兩次，大約每 MB 可執行檔 1 ms，MindNode 每點一次就要 260 ms。
- 最慢的一項掃描是最近使用的文件列表，775 筆記錄大約 230 ms，幾乎都花在逐筆檢查路徑上。

個別 App 還有自己的情況疊在上面，Xcode 的安裝收據有 41 MB，記著 12.5 萬條路徑，每次點開都要重新解析一遍，ChatGPT 會留下大約 15000 個暫存資料夾，每一個都要單獨確認是不是它的。

## 改了哪些地方

- cask 目錄在打開解除安裝頁時就在背景讀好，不用等到點擊。
- 點擊時只讀簽名資訊、不做校驗，並記下這個 App 套件的檔案標識，完整的簽名校驗放到列表出來以後在背景跑，刪除之前必須跑完。
- 最近使用的文件先按名字篩一遍，名字對得上的才做完整的歸屬檢查。
- 資源庫下的幾個根目錄平行列出，容器的身分按中繼資料檔快取，輔助程式的識別碼前綴每次掃描只算一次。
- 安裝收據按收據檔快取，第二次點 Xcode 不用再解析那 41 MB。
- 名字本身就能證明歸屬的暫存資料夾不再逐個確認，真正刪除時仍然逐個路徑檢查。
- 耗時長的掃描先開始，幾百個小任務排在後面。
- 滑鼠劃過時開始的預掃描，指標一離開就停下，解除安裝之前開始的掃描，也不會在解除安裝以後再把舊結果寫回來。

簽名校驗這一條，最省事的做法是直接刪掉，我沒刪，只是挪到了點擊以後。現在背景校驗沒通過時，App 本身照常解除安裝，只有靠簽名資訊才找到的那幾類殘留會留下來，比如按開發者團隊 ID 比對到的容器，結果裡會標成「無法確認 App 簽名」，沒刪也不算失敗。如果掃描以後 App 被換成了另一個，仍然會拒絕解除安裝。

## 改完以後快了多少

打開解除安裝頁大約 1.5 秒後第一次點擊，同樣是 Release 版本：

| App | 改之前 | 改之後 |
| --- | --- | --- |
| Shottr | 556 ms | 137 ms |
| MindNode | 1233 ms | 169 ms |
| Raycast | 716 ms | 171 ms |
| Parallels Desktop | 871 ms | 194 ms |
| VS Code | 584 ms | 232 ms |
| 微信 | 605 ms | 238 ms |
| Xcode | 3689 ms | 384 ms |
| ChatGPT | 6134 ms | 1530 ms |

大多數常見 App 現在都在 250 ms 以內，殘留一次列出來，不會先閃一下「正在掃描」，背景的簽名校驗在列表出來以後 12 到 434 ms 內跑完。

## ChatGPT 那一萬五千行

速度上來以後，展開 ChatGPT 往下捲還是卡，數了一下，它列出了 15175 行，其中 15143 行是 `T/.com.openai.codex.XXXXXX` 這種暫存資料夾，一共 1.0 GB，App 每次啟動都會留下幾個。我自己做的 Hina 也類似，344 行裡有 323 行是測試跑完留下的 `Hina-<Suite>-<UUID>.plist`，加起來才 0.01 MB，兩邊都是一種隨機命名的東西佔滿了整個列表。

卡頓除了行數多，還有兩個原因，展開的那一行每次重繪都要把所有殘留重新排一遍序，滑鼠停在列表上、行從指標下面滑過時，又會觸發整行重繪，於是每滑過一行就再排一次。拿真實的 ChatGPT 資料測，一次重繪從 309 到 354 ms 降到 56 到 58 ms，Hina 從 33 到 36 ms 降到 17 到 19 ms，ChatGPT 展開後的高度也從 3200 pt 縮到了 1225 pt。

現在幾行殘留如果是同一類、在同一個目錄、歸屬同一個 App，預設勾選也一樣，名字只差 App 自己產生的那一段，就會合成一行，這一段可以是隨機後綴、UUID、一長串十六進位或者當機報告裡的時間戳記。合併後的那一行保留類別名稱，路徑裡用 `*` 代替變化的部分，後面寫上數量和總大小，只有一個勾選框，也不再展開，ChatGPT 變成 13 個類別 33 行，Hina 變成 24 行。

<figure class="blog-diagram">
  <picture>
    <source media="(max-width: 900px)" srcset="/img/blog/uninstall-leftovers-one-line-mobile.webp">
    <img src="https://mole.fit/img/blog/uninstall-leftovers-one-line.webp" width="1600" height="992" loading="lazy" alt="同一份 ChatGPT 殘留的前後對比，改之前每個暫存資料夾各佔一行，改之後合成一行，寫著 15,143 項、1.02 GB，其餘殘留按類別列出。">
  </picture>
  <figcaption>同一份 ChatGPT 殘留，改之前一個暫存資料夾一行，改之後合成一行。</figcaption>
</figure>

有自己名字的兄弟目錄還是各佔一行，比如 ChatGPT 的七個 Electron 快取 `GPUCache`、`Code Cache`、`Crashpad` 這些，把它們也合併的話，Hina 只剩七行左右，看著反而不像清乾淨了，十幾行是比較合適的量。像 `T/<id>.helper` 這種名字裡帶個六個字母單字的，也單獨一行，因為只看這六個字母，分不清它是不是隨機後綴。

之前試過的兩種做法這次也沒再用，一種是在已經展開的 App 裡再套一層可以收合的分組，9 月 30 日試過，10 月 2 日就刪掉了，收合裡套收合，相似的路徑還會被分到分組和散行兩邊，另一種是一個類別只放一行，9 月 4 日用過，一週後又改回來，因為它把不同位置的東西藏到了一起。

## 別的解除安裝工具怎麼做

Pearcleaner 會平行列出大約 62 個根目錄，只按名字比對，不確認歸屬，也沒有快取，它快是因為做得少，騰訊檸檬在進入頁面時就把所有 App 的殘留算好，每天重新整理一次，點擊自然是瞬間的，代價是每次打開都有一次很大的背景掃描，和 Mole 不新增常駐開銷的方向不合，MangoDisk、Uninstally 和 Mole CLI 用的是固定的根目錄加有限的平行數。

翻下來沒有哪個工具有比 Mole 已經在用的 `getattrlistbulk` 和 `readdir` 更快的底層辦法。Spotlight 我也試過，查一次要 0.3 到 0.4 秒，還漏掉了大半殘留，VS Code 的四項只找到一項，`searchfs` 在 APFS 上能用，但一次要 15 秒左右，所以最後都是在現有的掃描上把重複的事拿掉。解除安裝之前怎麼看這份殘留列表，可以接著看[解除安裝前先看清楚殘留清單](https://mole.fit/tw/blog/mac-uninstall-leftovers-review-before-delete)。

---

Canonical HTML page: https://mole.fit/tw/blog/mole-uninstall-review-speed
Blog index for agents: https://mole.fit/tw/blog/llms.txt
Site index for agents: https://mole.fit/llms.txt
