NSFileCoordinator is een Foundation-klasse in iOS en macOS die veilige toegang tot bestanden biedt wanneer meerdere threads, processen of extensies tegelijkertijd werken. Volgens Apple Developer Documentation, 2024, voorkomt NSFileCoordinator race conditions bij het lezen en schrijven van bestanden, en garandeert dat geen enkel proces gegevens leest op het moment dat ze door een ander worden gewijzigd. De coördinator wordt gebruikt in iCloud Drive, File Provider Extension en alle multi-thread bestandsbewerkingen.
Belangrijkste punten
NSFileCoordinator — is een synchronisatiemechanisme voor bestandstoegang op besturingssysteemniveau, geïntroduceerd door Apple in iOS 5 en macOS 10.7 Lion. In tegenstelling tot traditionele vergrendelingen (NSLock, pthread_mutex), werkt de coördinator op bestandssysteemniveau en kan toegang coördineren tussen verschillende processen, niet alleen tussen threads van één applicatie.
De noodzaak voor NSFileCoordinator komt voort uit de Sandbox-architectuur in iOS: elk proces (app, extensie, systeemdienst) werkt in een geïsoleerde omgeving met eigen toegang tot bestanden. Wanneer meerdere processen tegelijkertijd proberen hetzelfde bestand te lezen en te schrijven (bijvoorbeeld bij iCloud Drive-synchronisatie), ontstaan zonder coördinator race conditions: proces A leest het bestand op het moment dat proces B het al gedeeltelijk heeft overschreven.
Volgens WWDC 2023 raadt Apple ten zeerste aan om NSFileCoordinator te gebruiken voor alle bestandsbewerkingen in Ubiquity container (iCloud Drive) en bij het werken met File Provider Extension. Het negeren van coördinatie is een van de veelvoorkomende oorzaken van gegevensbeschadiging en niet-reproduceerbare bugs in iOS-applicaties.
Coördinatie-intentie (NSFileCoordinator.ReadingIntent / WritingIntent) — is een object dat het type bewerking declareert dat een thread of proces van plan is uit te voeren. De coördinator gebruikt deze intenties om de toegangsvolgorde te bepalen en conflicten op te lossen.
| Type intentie | Beschrijving | Wanneer gebruiken |
|---|---|---|
| ReadingIntent | Bestand lezen zonder wijzigingen | Document openen, gegevens laden |
| WritingIntent | Schrijven met mogelijke inhoudswijziging | Document opslaan, bewerken |
| ReadingIntent(URL, options: .withoutChanges) | Lezen zonder wijzigingen te volgen | Snelle weergave van inhoud |
| WritingIntent(URL, options: .contentIndependentMetadataOnly) | Alleen metadata wijzigen | Datum of attributen bijwerken |
| WritingIntent(URL, options: .forDeleting) | Bestand verwijderen | Document verwijderen door gebruiker |
Coördinatieregels: meerdere gelijktijdige lezingen zijn toegestaan (als er geen actief schrijven is), schrijven is exclusief — geen lezen of schrijven is toegestaan tijdens een schrijfbewerking. Dit komt overeen met het readers-writer lock-model, maar met extra ondersteuning voor interprocescoördinatie via launchd en XPC.
Belangrijke nuance: NSFileCoordinator voorkomt geen toegang tot het bestand via gewone NSData of FileManager — het coördineert alleen bewerkingen die expliciet in coördinatieblokken zijn ingepakt. Als een andere thread rechtstreeks toegang krijgt tot het bestand, waarbij de coördinator wordt omzeild, ontstaan precies die race conditions die de coördinator zou moeten voorkomen.
Basispatroon voor het gebruik van NSFileCoordinator bestaat uit drie stappen: een instantie van de coördinator maken, de intentie declareren (lezen of schrijven) en de bewerking uitvoeren binnen het coördinatieblok. De coördinator garandeert dat geen andere coördinator tegelijkertijd met hetzelfde bestand werkt.
import Foundation
let coordinator = NSFileCoordinator()
let fileURL = getDocumentURL()
// Veilig lezen
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)
}
// Veilig schrijven
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)"
)
}
}
Batchbewerking — de coördinator kan meerdere bestanden in één bewerking verwerken met behulp van een reeks intenties. Dit is handig voor het verplaatsen, kopiëren of verwijderen van een set bestanden als één transactie. Als een van de intenties niet kan worden uitgevoerd, wordt de hele bewerking met een fout geannuleerd.
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 coördinatie — sinds iOS 15 ondersteunt NSFileCoordinator asynchrone methoden met completion handler, waardoor coördinatie kan worden uitgevoerd zonder de aanroepende thread te blokkeren. Dit is van cruciaal belang voor de UI-thread, waar synchroon wachten op coördinatie de interface secondenlang kan laten vastlopen.
NSFilePresenter — is een protocol dat een object implementeert om meldingen te ontvangen over wijzigingen in bestanden die door NSFileCoordinator worden gecoördineerd. Als uw applicatie de inhoud weergeeft van een bestand dat door een ander proces kan worden gewijzigd (bijvoorbeeld iCloud Drive synchroniseert een nieuwe versie), maakt implementatie van NSFilePresenter tijdige bijwerking van de interface mogelijk.
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)
}
}
Protocoldmethoden: presentedItemDidChange wordt aangeroepen bij wijziging van bestandsinhoud, presentedItemDidMove(to:) — na verplaatsing van het bestand, accommodatePresentedItemDeletion — vóór verwijdering van het bestand door een ander proces (laat de applicatie het bestand correct sluiten). Daarnaast ondersteunt het protocol versiebeheer via presentedItemDidGainVersion: en presentedItemDidLoseVersion:.
Belangrijk: NSFilePresenter moet in het systeem worden geregistreerd via NSFileCoordinator.addFilePresenter:. Zonder registratie worden meldingen niet afgeleverd. Registratie wordt eenmalig uitgevoerd bij het starten van de applicatie en vereist geen herregistratie bij het opnieuw aanmaken van de presenter.
Gebruik altijd de coördinator voor bestanden in Ubiquity container (iCloud Drive) en mappen die toegankelijk zijn voor extensies. Zelfs als de applicatie momenteel single-thread is, kunnen toekomstige updates of systeemwijzigingen parallelle toegang toevoegen, en het ontbreken van coördinatie zal leiden tot moeilijk te vinden bugs.
Minimaliseer de tijd in het coördinatieblok. Terwijl het blok wordt uitgevoerd, hebben andere processen geen toegang tot het bestand. Lange bewerkingen binnen het blok (complexe gegevensverwerking, netwerkverzoeken) blokkeren het hele bestandstoegangssysteem. Voer alleen het lezen of schrijven van gegevens binnen het blok uit en de verwerking erbuiten.
Vermijd deadlocks: roep de coördinator niet aan vanuit een coördinatieblok van een andere coördinator voor hetzelfde bestand — dit leidt tot wederzijdse blokkering. Gebruik batchbewerkingen (reeks intenties) in plaats van geneste aanroepen. Als nesting noodzakelijk is, gebruik dan verschillende wachtrijen of verschillende URL's.
Volgens objc.io (2024) omvatten typische fouten bij het werken met NSFileCoordinator: het ontbreken van foutafhandeling in completion handler (leidt tot onvoltooide bewerkingen); coördinatie alleen voor schrijven, maar niet voor lezen; gebruik van verouderde synchrone API in de UI-thread; negeren van het NSFilePresenter-protocol bij werken met iCloud Drive. De laatste fout is het meest verraderlijk: de applicatie toont verouderde gegevens zonder te weten dat het bestand al is gewijzigd.
Veelgestelde vragen
NSFileCoordinator — Foundation-klasse voor veilige toegang tot bestanden vanuit meerdere threads of processen. Het voorkomt race conditions door lees- en schrijfbewerkingen op bestandssysteemniveau te coördineren.
NSLock werkt alleen binnen één proces (tussen threads). NSFileCoordinator coördineert toegang tussen verschillende processen en extensies, inclusief iCloud Drive-synchronisatie en File Provider Extension.
Ja, Apple raadt ten zeerste aan NSFileCoordinator te gebruiken voor alle bewerkingen met Ubiquity container-bestanden. Zonder coördinator zijn gegevensbeschadiging tijdens synchronisatie tussen apparaten en conflicten met File Provider Extension mogelijk.
NSFilePresenter — protocol voor het ontvangen van meldingen over bestandswijzigingen. Hiermee kan de applicatie reageren op wijzigingen door andere processen: UI bijwerken bij wijziging, verplaatsing afhandelen of zich voorbereiden op verwijdering van het bestand.
Vijf typen: ReadingIntent (lezen), WritingIntent (schrijven), ReadingIntent met .withoutChanges (lezen zonder volgen), WritingIntent met .contentIndependentMetadataOnly (alleen metadata) en WritingIntent met .forDeleting (verwijderen). Elk bepaalt het toegangsniveau tot het bestand.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook