Skip to content

ファイル辞書のメモリリークを修正 - #544

Merged
mtgto merged 1 commit into
mainfrom
fix-filedict-presenter-leak
Nov 4, 2026
Merged

mtgto merged 1 commit into
mainfrom
fix-filedict-presenter-leak

Conversation

@mtgto

@mtgto mtgto commented Nov 3, 2026

Copy link
Copy Markdown
Owner

概要

設定画面で辞書を有効・無効にしたり、エンコーディングや「変換候補をユーザー辞書に保存する」の設定を変えたりするたびに、古い FileDict が解放されずに残ってしまうメモリリークがありました。この問題を修正します。

原因

FileDict は init で NSFileCoordinator.addFilePresenter(self) を呼び、deinit で removeFilePresenter(self) を呼んでいました。
ですが NSFileCoordinator は登録されたNSFilePresenterを強参照で持つので、登録されている限り deinit は呼ばれず、解除もされません。

あわせて、with(saveToUserDict:) で作り直した FileDict はNSFilePresenterに登録されていませんでした。そのため、設定を変えたあとの辞書は外部での編集を受け取れず、通知はリークした古いインスタンスに届いていました。

変更内容

  • FileDict を NSFilePresenter として登録しないようにしました。登録簿から FileDict への参照がなくなるので、解除し忘れや弱参照の付け忘れでリークすることもありません。
  • そのかわり、もともと辞書フォルダを監視している UserDict が、presentedSubitemDidChange(at:) で辞書フォルダ内のファイルの変更を受けたときに、該当する FileDict の fileDidChange(modificationDate:) を呼ぶようにしました。
    • 通知のたびに、その時点の dicts(またはユーザー辞書)からファイル名で FileDict を探します。そのため、with(saveToUserDict:) で置き換えたあとの辞書にも変更が届きます。
    • ユーザー辞書の変更も同じ経路で伝えます。ただし、ユーザー辞書についてはこれまでどおり notificationNameDictFileDidAppear を投げません(ファイル辞書の一覧に追加しないため)。
  • with(saveToUserDict:) では fileModificationDate を引き継ぐようにしました。自分の保存による変更をスキップする判定を、作り直したあとも正しく行うためです。
  • load() / save() で NSFileCoordinator に渡すファイルプレゼンターを nil にしました。これまで渡していた FileDict 自身はもう登録していないので、挙動は変わりません。

検討したほかの案

  • FileDict ごとに専用のプレゼンター(FileDict を弱参照で持つ)を登録する案も考えました。リークは直りますが、弱参照に頼る作りになります。また、with で作り直すたびに、古いインスタンスが解放されるまでは同じファイルを2つのNSFilePresenterが監視することになるので採用しませんでした。

今後の課題

  • フォルダの監視は辞書フォルダの中にしか届きません。今後、App Sandbox のコンテナ外にある辞書(Security-scoped bookmark)に対応する場合は、UserDict 側に外部の辞書ごとのプレゼンターを持たせる必要があります。

テスト

  • FileDictTests
    • 参照を手放すと FileDict が解放される
    • fileDidChange(modificationDate:) は更新日時が変わっていれば読み直し、前回と同じならスキップする
    • with(saveToUserDict:) で置き換えた辞書が読み直せて、読み直しても「ユーが変わらない
  • UserDictTests.testReloadFileDictWhenFileChanged
    • 辞書フォルダ内のファイルを別の NSFileCoordinator から書き換えると、`di される
    • with で置き換えたあとは、新しい辞書だけが読み直される

実機での確認

  • SKK-JISYO.L の有効・無効を10回繰り返しても、Activity Monitor でメモリが増えていかないこと。「辞書 … がプロセスから削除されます」のログが出ること
  • 外部エディタで有効な辞書を編集すると、読み直されること
  • 「変換候補をユーザー辞書に保存する」を切り替えた辞書を外部で編集しても、変更が反映されること
  • 単語登録で保存したときは読み込みがスキップされ、ユーザー辞書を外部で編集したときは読み直されること

NSFileCoordinatorは登録したファイルプレゼンターを強参照で保持するため、
FileDictが自分自身を登録してdeinitで解除する作りでは解放されず、
辞書の無効化やエンコーディング変更、saveToUserDictの変更のたびに古い辞書がリークしていた。
また、saveToUserDictを変更したあとの辞書はファイルの変更を受け取れていなかった。

辞書フォルダを監視しているUserDictが、フォルダ内のファイルの変更をその時点のFileDictに伝えるようにする。
@mtgto
mtgto merged commit 68b1082 into main Nov 4, 2026
1 check passed
@mtgto
mtgto deleted the fix-filedict-presenter-leak branch November 4, 2026 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant