NSFileCoordinator ist eine Foundation-Klasse in iOS und macOS, die sicheren Dateizugriff gewährleistet, wenn mehrere Threads, Prozesse oder Erweiterungen gleichzeitig arbeiten. Laut der Apple Developer Documentation, 2024 verhindert NSFileCoordinator Wettlaufsituationen beim Lesen und Schreiben von Dateien und garantiert, dass kein Prozess Daten liest, während ein anderer sie ändert. Der Koordinator wird in iCloud Drive, File Provider Extension und allen Multithread-Dateioperationen verwendet.
Wichtige Erkenntnisse
NSFileCoordinator ist ein Synchronisationsmechanismus für Dateizugriffe auf Betriebssystemebene, der von Apple in iOS 5 und macOS 10.7 Lion eingeführt wurde. Im Gegensatz zu traditionellen Sperren (NSLock, pthread_mutex) arbeitet der Koordinator auf Dateisystemebene und kann den Zugriff zwischen verschiedenen Prozessen koordinieren, nicht nur zwischen Threads derselben Anwendung.
Die Notwendigkeit für NSFileCoordinator ergibt sich aus der Sandbox-Architektur in iOS: Jeder Prozess (Anwendung, Erweiterung, Systemdienst) läuft in einer isolierten Umgebung mit eigenem Dateizugriff. Wenn mehrere Prozesse gleichzeitig versuchen, dieselbe Datei zu lesen und zu schreiben (z.B. während der iCloud Drive-Synchronisation), entstehen ohne Koordinator Wettlaufsituationen: Prozess A liest die Datei, während Prozess B sie bereits teilweise überschrieben hat.
Laut WWDC 2023 empfiehlt Apple dringend die Verwendung von NSFileCoordinator für alle Dateioperationen im Ubiquity-Container (iCloud Drive) und bei der Arbeit mit File Provider Extension. Das Ignorieren der Koordination ist eine der häufigsten Ursachen für Datenkorruption und nicht reproduzierbare Fehler in iOS-Anwendungen.
Koordinationsabsicht (NSFileCoordinator.ReadingIntent / WritingIntent) ist ein Objekt, das den Typ der Operation deklariert, die ein Thread oder Prozess ausführen möchte. Der Koordinator verwendet diese Absichten, um die Zugriffsreihenfolge zu bestimmen und Konflikte zu lösen.
| Absichtstyp | Beschreibung | Verwendungszweck |
|---|---|---|
| ReadingIntent | Dateilesen ohne Änderungen | Dokument öffnen, Daten laden |
| WritingIntent | Schreiben mit möglicher Inhaltsänderung | Dokument speichern, bearbeiten |
| ReadingIntent(URL, options: .withoutChanges) | Lesen ohne Änderungsverfolgung | Schnelle Inhaltsvorschau |
| WritingIntent(URL, options: .contentIndependentMetadataOnly) | Nur Metadaten ändern | Datum oder Attribute aktualisieren |
| WritingIntent(URL, options: .forDeleting) | Datei löschen | Dokumentlöschung durch Benutzer |
Koordinationsregeln: Mehrere gleichzeitige Lesevorgänge sind erlaubt (wenn kein aktives Schreiben stattfindet), Schreiben ist exklusiv — während eines Schreibvorgangs sind weder Lese- noch Schreibvorgänge erlaubt. Dies folgt dem Readers-Writer-Lock-Modell, jedoch mit zusätzlicher Unterstützung für prozessübergreifende Koordination über launchd und XPC.
Wichtige Nuance: NSFileCoordinator verhindert keinen Dateizugriff über normale NSData oder FileManager — er koordiniert nur Operationen, die explizit in Koordinationsblöcke eingeschlossen sind. Wenn ein anderer Thread ohne den Koordinator direkt auf die Datei zugreift, treten genau die Wettlaufsituationen auf, die der Koordinator verhindern soll.
Grundlegendes Muster zur Verwendung von NSFileCoordinator besteht aus drei Schritten: Koordinatorinstanz erstellen, eine Absicht deklarieren (Lesen oder Schreiben) und die Operation innerhalb eines Koordinationsblocks ausführen. Der Koordinator garantiert, dass kein anderer Koordinator gleichzeitig mit derselben Datei arbeitet.
import Foundation
let coordinator = NSFileCoordinator()
let fileURL = getDocumentURL()
// Safe reading
let readIntent = NSFileCoordinator
.ReadingIntent(url: fileURL)
var content: Data?
var readError: NSError?
coordinator.coordinate(with: readIntent) { error in
if let error = error {
readError = error
return
}
content = try? Data(contentsOf: fileURL)
}
// Safe writing
let writeIntent = NSFileCoordinator
.WritingIntent(url: fileURL)
coordinator.coordinate(with: writeIntent) { error in
guard error == nil else { return }
do {
try newData.write(to: fileURL)
} catch {
Logger.storage.error(
"Write failed: \(error)"
)
}
}
Batch-Operation — der Koordinator kann mehrere Dateien in einer einzigen Operation mit einem Array von Absichten verarbeiten. Dies ist praktisch zum Verschieben, Kopieren oder Löschen einer Gruppe von Dateien als einzelne Transaktion. Wenn eine Absicht nicht erfüllt werden kann, wird die gesamte Operation mit einem Fehler abgebrochen.
let coordinator = NSFileCoordinator()
let readIntent = NSFileCoordinator
.ReadingIntent(url: sourceURL)
let writeIntent = NSFileCoordinator
.WritingIntent(url: destURL)
coordinator.coordinate(
with: [readIntent, writeIntent]
) { error in
try? FileManager.default
.copyItem(at: sourceURL, to: destURL)
}
Asynchrone Koordination — ab iOS 15 unterstützt NSFileCoordinator asynchrone Methoden mit einem Completion-Handler, was Koordination ohne Blockierung des aufrufenden Threads ermöglicht. Dies ist entscheidend für den UI-Thread, wo synchrone Wartezeit auf Koordination die Oberfläche für Sekunden einfrieren kann.
NSFilePresenter ist ein Protokoll, das ein Objekt implementiert, um Benachrichtigungen über Änderungen an Dateien zu erhalten, die von NSFileCoordinator koordiniert werden. Wenn Ihre Anwendung den Inhalt einer Datei anzeigt, die von einem anderen Prozess geändert werden kann (z.B. iCloud Drive synchronisiert eine neue Version), ermöglicht die Implementierung von NSFilePresenter eine rechtzeitige Aktualisierung der Oberfläche.
class DocumentPresenter: NSFilePresenter {
let presentedItemURL: URL?
let presentedItemOperationQueue: OperationQueue
init(url: URL) {
presentedItemURL = url
presentedItemOperationQueue = OperationQueue()
}
func presentedItemDidChange() {
DispatchQueue.main.async {
NotificationCenter.default
.post(name: .documentDidChange,
object: self)
}
}
func presentedItemDidMove(to newURL: URL) {
Logger.storage.info(
"File moved to: \(newURL.lastPathComponent)"
)
}
func accommodatePresentedItemDeletion(
completionHandler: @escaping (Error?) -> Void
) {
Logger.storage.warn("File deleted externally")
completionHandler(nil)
}
}
Protokollmethoden: presentedItemDidChange wird aufgerufen, wenn sich der Dateiinhalt ändert, presentedItemDidMove(to:) — nach der Dateiverschiebung, accommodatePresentedItemDeletion — vor dem Löschen der Datei durch einen anderen Prozess (ermöglicht der Anwendung, die Datei ordnungsgemäß zu schließen). Zusätzlich unterstützt das Protokoll Versionierung über presentedItemDidGainVersion: und presentedItemDidLoseVersion:.
Wichtig: NSFilePresenter muss über NSFileCoordinator.addFilePresenter: im System registriert werden. Ohne Registrierung werden Benachrichtigungen nicht zugestellt. Die Registrierung erfolgt einmalig beim Start der Anwendung und erfordert keine erneute Registrierung, wenn der Presenter neu erstellt wird.
Verwenden Sie immer den Koordinator für Dateien im Ubiquity-Container (iCloud Drive) und Verzeichnissen, die für Erweiterungen zugänglich sind. Selbst wenn die Anwendung derzeit single-threaded ist, können zukünftige Updates oder Systemänderungen parallelen Zugriff hinzufügen, und das Fehlen von Koordination führt zu schwer auffindbaren Fehlern.
Minimieren Sie die Zeit im Koordinationsblock. Während der Block ausgeführt wird, können andere Prozesse nicht auf die Datei zugreifen. Lange Operationen innerhalb des Blocks (komplexe Datenverarbeitung, Netzwerkanfragen) blockieren das gesamte Dateizugriffssystem. Führen Sie nur Lese- oder Schreibvorgänge innerhalb des Blocks durch und die Verarbeitung außerhalb.
Vermeiden Sie Deadlocks: Rufen Sie den Koordinator nicht von innerhalb des Blocks eines anderen Koordinators für dieselbe Datei auf — dies führt zu einem gegenseitigen Deadlock. Verwenden Sie Batch-Operationen (Array von Absichten) anstelle von verschachtelten Aufrufen. Wenn Verschachtelung erforderlich ist, verwenden Sie verschiedene Warteschlangen oder verschiedene URLs.
Laut objc.io (2024) gehören zu den typischen Fehlern bei der Arbeit mit NSFileCoordinator: fehlende Fehlerbehandlung im Completion-Handler (führt zu unvollständigen Operationen); Koordination nur für Schreib-, aber nicht für Lesevorgänge; Verwendung der veralteten synchronen API im UI-Thread; Ignorieren des NSFilePresenter-Protokolls bei der Arbeit mit iCloud Drive. Der letzte Fehler ist der tückischste: Die Anwendung zeigt veraltete Daten an, ohne zu wissen, dass die Datei bereits geändert wurde.
Häufig gestellte Fragen
NSFileCoordinator ist eine Foundation-Klasse für sicheren Dateizugriff von mehreren Threads oder Prozessen. Er verhindert Wettlaufsituationen durch Koordination von Lese- und Schreiboperationen auf Dateisystemebene.
NSLock funktioniert nur innerhalb eines einzelnen Prozesses (zwischen Threads). NSFileCoordinator koordiniert den Zugriff zwischen verschiedenen Prozessen und Erweiterungen, einschließlich iCloud Drive-Synchronisation und File Provider Extension.
Ja, Apple empfiehlt dringend die Verwendung von NSFileCoordinator für alle Dateioperationen im Ubiquity-Container. Ohne Koordinator kann es während der Synchronisation zwischen Geräten zu Datenkorruption und Konflikten mit File Provider Extension kommen.
NSFilePresenter ist ein Protokoll zum Empfangen von Benachrichtigungen über Dateiänderungen. Es ermöglicht der Anwendung, auf Änderungen durch andere Prozesse zu reagieren: UI bei Modifikation aktualisieren, Verschiebung behandeln oder sich auf das Löschen von Dateien vorbereiten.
Fünf Typen: ReadingIntent (Lesen), WritingIntent (Schreiben), ReadingIntent mit .withoutChanges (Lesen ohne Verfolgung), WritingIntent mit .contentIndependentMetadataOnly (nur Metadaten) und WritingIntent mit .forDeleting (Löschen). Jeder definiert die Zugriffsebene auf die Datei.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch