爱意满满的作品展示区。
mahui

Diskly 1.5.0 发版 —— 我做的 macOS 磁盘分析与清理工具

  •  
  •   mahui · 5h 5m ago · 198 views

    大家好。我做了一个 macOS 原生的磁盘分析 + 清理工具 Diskly,今天发了 1.5.0,顺便来 V2 说几句。

    Diskly 仪表盘

    为什么又造一个轮子

    DaisyDisk / GrandPerspective 都用过。两件事我一直介意:

    • 数字会骗人。iCloud / OneDrive 里没下载的占位文件,系统会按"逻辑大小"算几十 GB——但你删掉根本腾不出空间。macOS 的 firmlink (/Users 实际上是 /System/Volumes/Data/Users 的另一条路径)和硬链接会让同一份数据被算两遍——我的 2 TB 盘曾经被某工具扫出 6.46 TB
    • 看清和动手是两件事。可视化做完,清理这步要么没有,要么直接把你引去一堆"建议",一键删完你都不知道删了啥。

    所以 Diskly 的几个产品决策都围绕这两点。

    几件我较真的事

    1. 数字必须诚实

    • 全程按 本地实际占用totalFileAllocatedSize)算,云占位文件计 0 。
    • firmlink + 硬链接按 (st_dev, st_ino) 整数键去重,目录全去重 / 文件仅当 linkCount > 1 时去重(常态零开销)。
    • 实测扫 /:6.46 TB → 1.62 TB,与 df 报告基本一致(剩下的差额是 APFS clone 的固有现象,不假装修)。

    2. 多入口起步,扫过的目录秒回

    新建扫描页一屏装五种入口:拖拽 / ⌘O / 菜单 / Finder 服务 / 快速访问 + 最近扫描。

    新建扫描页

    顶部的「 Pick up where you left off 」是 1.4.x 加的——扫过的结果还在内存里时,点一下秒回(无需重扫);多份结果按内存预算淘汰,当前这份永不淘汰。

    3. 等待也该有信息

    扫几百万文件需要时间,但进度条不该是空白的

    1.5.0 里把扫描中页的下半区改了:加了一张实时构成图——按已识别字节的分类铺一张 mini 矩形树图,跟扫描结束后的总览同款配色。等待时已经能看到"占大头的是啥类型",体验连续。

    实现细节:复用 Treemap.layout 的 squarified 算法,9 个分类桶合成成一棵浅层 FileNode 树喂进去布局。每帧重建 9 节点 + 一次 squarify,O(9) 极快不会成为热点。

    4. 清理是有分寸的

    清理建议分两轨:安全可删 / 建议检查。默认只勾安全的。

    • 安全档:DerivedData 、~/Library/Caches、SwiftPM .build、Xcode iOS DeviceSupport 、CoreSimulator 缓存——可自动重建。
    • 检查档:node_modules 、长期未用应用、Docker 镜像——需要你自己判断。

    清理建议

    差异化的几个点:

    • node_modules 用 .git/logs/HEAD 修改时间判断仓库活跃度,< 90 天自动排除(不调 git 子进程,避免触发 CLT 安装弹窗)。
    • 应用最近使用走 Spotlight kMDItemLastUsedDate,取不到不猜(诚实降级,不编"很久没打开了")。
    • 全部移到废纸篓,30 天内可恢复。

    并且每条建议都能展开看实际要删的文件清单,删前可以逐条核对、单独取消:

    展开核对每一项

    5. 克制,且只属于你

    • 菜单栏只占一个图标 + 一个百分比,点开才展开。
    • 零遥测。不弹广告、不催升级、不收集任何数据。
    • 经 Apple 公证、官网直接分发。

    1.5.0 干了啥

    这一版改动比较大,主要是性能 + UX 修复。挑几个有意思的说:

    树图悬停帧率。之前 Canvas 同时画结构和高亮,鼠标每动一像素整张树图重绘 + 每个标签跑二分测量。拆成「结构层 + 高亮层」两层 Canvas,.equatable() 让 SwiftUI 在悬停时跳过结构层重渲染;标签宽度在 rebuild 时一次性 NSAttributedString.size() 二分截断好,绘制零度量。

    下钻 /Applications 不再等几十秒。Spotlight 元数据查询从串行改 DispatchQueue.concurrentPerform 并发 + URL-keyed 会话缓存。200~500 个 app × 30~80ms 串行 = 6~40s,改并发后摊到核数,体感明显。

    内存底座 −150 MBFileNode.idUUID()ObjectIdentifier(self)——900 万节点 × 16 字节 UUID 是纯开销,ObjectIdentifier 是 class 自带的零开销 identity,Identifiable 协议照样满足。

    ETA 终于准了。旧算法用瞬时速率 EMA × 剩余字节,早期抖动剧烈、末期对小波动极敏感。改成「已用时间外推」:elapsed × (1 - fraction) / fraction,稳得多。同时单调钳制下降即时反映、上升每帧最多 +5s,避免视觉抖动;前 3 秒不显示(预热),整理阶段不显示。

    ETA 文案 i18n 修复Formatting.duration 之前硬编码"秒/分/小时",英文模式下泄漏"2 分 48 秒"。改用 DateComponentsFormatter,按 locale 自动出"2m 48s"/"2 分 48 秒"。

    菜单栏「偏好设置…」真能打开了。之前点了没反应——NSApp.sendAction(Selector(("showSettingsWindow:"))) 在 popover dismiss 同步链不可靠,SwiftUI Settings scene 的注入 target 找不到。新实现 DispatchQueue.main.async 让 popover 先彻底关闭,然后按 ⌘, 快捷键 递归找主菜单 item 直接 performActionForItem(等价于用户从 App 菜单点击)。

    无默认邮件客户端兜底。这是 V2 提醒我考虑的:只用网页 Gmail / Outlook 的用户点 mailto 会失败。SupportContact.openOrCopyNSWorkspace.open,失败时复制邮箱到剪贴板 + 弹原生 alert;设置里每处邮箱旁加独立「复制」小按钮。

    完整 changelog: https://diskly.app.mahui.me/changelog

    怎么装

    brew install --cask mahui/tap/diskly
    

    或者官网 https://diskly.app.mahui.me 直接下 .dmg

    Apple 公证 + 时间戳签名,Gatekeeper 直接放行,不用右键打开。要求 macOS 14 Sonoma+,Universal 二进制(Apple Silicon + Intel)。

    价格

    $9.99 一次买断,5 台设备激活。14 天全功能试用,试用结束才需要付费。

    V2 老司机如果想体验完整版,私信我邮箱给一把激活码(几把够 V2er 用没问题)。

    反馈

    —— 昨天发了 Pier(菜单栏看端口/进程的小工具),今天又来打扰,如有问题欢迎拍。


    技术栈:Swift + SwiftUI 原生,SPM 可执行文件(不是 Xcode 工程)。扫描器固定 worker 池 + 共享队列分治,实测扫 935 万文件的 / 大约 90s 左右,峰值内存 ~5 GB 。

    No Comments Yet
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   1038 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 48ms · UTC 18:41 · PVG 02:41 · LAX 11:41 · JFK 14:41
    ♥ Do have faith in what you're doing.