ああ、自動ファイル ロックです。
これは、しばらくアクセスされていない自動保存ドキュメントで発生します。
典型的なアプローチは、コア データ スタックを作成する前にロックに気づき、ユーザーにファイルのロックを解除するように求めるダイアログを表示することです。
彼らがファイルのロックを解除することに同意した場合は、単にロックを解除して通常どおり実行します。
ロックを解除することに同意しない場合は、コピーするか、読み取り専用で開きます。もちろん、単純にユーザーの設定をバイパスしてファイルのロックを自動的に解除することもできますが、それはおそらくあまり良い方法ではありません。
ファイルがロックされているかどうかを判断し、ファイルをロック/ロック解除するのに役立つカテゴリを次に示します。
これは、ファイルモードが読み取り専用に変更されることとは完全に別のものですが、同様の方法で処理できることに注意してください。
カテゴリ インターフェイス
@interface NSFileManager (MyFileLocking)
- (BOOL)isFileLockedAtPath:(NSString *)path;
- (BOOL)unlockFileAtPath:(NSString*)path error:(NSError**)error;
- (BOOL)lockFileAtPath:(NSString*)path error:(NSError**)error;
@end
カテゴリの実装
@implementation NSFileManager (MyFileLocking)
- (BOOL)isFileLockedAtPath:(NSString *)path {
return [[[self attributesOfItemAtPath:path error:NULL]
objectForKey:NSFileImmutable] boolValue];
}
- (BOOL)unlockFileAtPath:(NSString*)path error:(NSError**)error {
return [self setAttributes:@{NSFileImmutable:@NO}
ofItemAtPath:path
error:error];
}
- (BOOL)lockFileAtPath:(NSString*)path error:(NSError**)error {
return [self setAttributes:@{NSFileImmutable:@YES}
ofItemAtPath:path
error:error];
}
@end
次に、ロックされているかどうかを確認するために呼び出し[[NSFileManager defaultManager] isFileLockedAtPath:path]
、ロックされている場合は、ユーザーに対処方法を尋ねるダイアログをスローします。その後、ロックを解除してスタックを通常どおり開くか、ロックしたままスタックを読み取り専用で開くことができます。これにより、保存によってファイル ストアが変更されるのを防ぐことができます。
ファイルを監視して、ロック/ロック解除からいつ変更されたかを知り、それに応じて対応することもできることに注意してください。
これに関する Apple のガイドラインについては、https://developer.apple.com/library/mac/documentation/DataManagement/Conceptual/DocBasedAppProgrammingGuideForOSX/StandardBehaviors/StandardBehaviors.htmlを参照してください。
編集
Ok。NSPersistentDocument が NSDocument の動作を複製することを望んでいました-編集が試行されたときにのみロック解除のプロンプトが表示されます。あなたが言っているのは、NSPersistentDocument にはそのような機能がないということですか? – アダーシュテット
わかった。読み取り/書き込みで開くことができるように、ユーザーにロックを解除するように依頼したいと考えていました。
「フローに沿って」、必要に応じて読み取り専用で開きたい場合は、NSPersistentDocument
サブクラスに少しカスタマイズを追加する必要があります。
最初に、元のオプションで読み取り専用ファイルが指定されているかどうかを追跡するための小さな状態を追加します。
@implementation MyDocument {
BOOL explicitReadOnly;
}
次に、いくつかのユーティリティメソッドが必要になります...
- (NSDictionary*)addReadOnlyOption:(NSDictionary*)options {
NSMutableDictionary *mutable = options ? [options mutableCopy]
: [NSMutableDictionary dictionary];
mutable[NSReadOnlyPersistentStoreOption] = @YES;
return [mutable copy];
}
- (NSDictionary*)removeReadOnlyOption:(NSDictionary*)options {
NSMutableDictionary *mutable = options ? [options mutableCopy]
: [NSMutableDictionary dictionary];
[mutable removeObjectForKey:NSReadOnlyPersistentStoreOption];
return [mutable copy];
}
次に、独自の永続ストア コーディネーター構成コードを提供します。これにより、作成時にストアに読み取り専用オプションを提供できます。このメソッドは、ドキュメントをビルドするときに自動的に呼び出されます。オーバーライドの実装を提供するだけで済みます。
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url
ofType:(NSString *)fileType
modelConfiguration:(NSString *)configuration
storeOptions:(NSDictionary<NSString *,id> *)storeOptions
error:(NSError * _Nullable __autoreleasing *)error {
explicitReadOnly = [storeOptions[NSReadOnlyPersistentStoreOption] boolValue];
if (![[NSFileManager defaultManager] isWritableFileAtPath:url.path]) {
storeOptions = [self addReadOnlyOption:storeOptions];
}
return [super configurePersistentStoreCoordinatorForURL:url
ofType:fileType
modelConfiguration:configuration
storeOptions:storeOptions
error:error];
}
また、 がプロトコルをNSPersistentDocument
実装していることに注意してください。NSFilePresenter
したがって、メソッドをオーバーライドして、ファイルの内容や属性が変更されるたびに通知を受けることができます。これにより、アプリケーション、Finder、またはその他のメカニズム内からのロック/ロック解除を含む、ファイルへの変更が通知されます。
- (void)presentedItemDidChange {
[self ensureReadOnlyConsistency];
[super presentedItemDidChange];
}
次に、永続ストアがファイルの読み取り専用プロパティと一貫性を保つようにします。
readOnly
これは、ストアのプロパティを変更するだけの 1 つの実装です。
- (void)ensureReadOnlyConsistency {
NSURL *url = [self presentedItemURL];
BOOL fileIsReadOnly = ![[NSFileManager defaultManager] isWritableFileAtPath:url.path];
NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;
[psc performBlock:^{
NSPersistentStore *store = [psc persistentStoreForURL:url];
if (store) {
if (fileIsReadOnly) {
if (!store.isReadOnly) {
store.readOnly = YES;
}
} else if (!explicitReadOnly) {
if (store.isReadOnly) {
store.readOnly = NO;
}
}
}
}];
}
これは機能しますが、1 つの小さなハングアップがあります。ストアが最初に読み取り専用オプションで開かれた場合、最初にreadOnly
属性が NO に設定されたときに、最初の保存がスローされます (実際にはobtainPermanentIDsForObjects:error:
呼び出しです。コア データは例外をキャッチしているように見えますが、コンソールに記録されます)。 .
保存は続行され、問題はないようです。すべてのオブジェクトが保存され、オブジェクト ID も適切に取得されて記録されます。
だから、私が言えることは何もうまくいかないということはありません。
ただし、別のより厳格なオプションがありますが、前述の「問題」を回避できます。ストアを交換できます。
- (void)ensureReadOnlyConsistency {
NSURL *url = [self presentedItemURL];
BOOL fileIsReadOnly = ![[NSFileManager defaultManager] isWritableFileAtPath:url.path];
NSPersistentStoreCoordinator *psc = self.managedObjectContext.persistentStoreCoordinator;
[psc performBlock:^{
NSPersistentStore *store = [psc persistentStoreForURL:url];
if (store) {
if (fileIsReadOnly != store.isReadOnly) {
NSString *type = store.type;
NSString *configuration = store.configurationName;
NSDictionary *options = store.options;
if (fileIsReadOnly) {
options = [self addReadOnlyOption:options];
} else if (!explicitReadOnly) {
options = [self removeReadOnlyOption:options];
}
NSError *error;
if (![psc removePersistentStore:store error:&error] ||
![psc addPersistentStoreWithType:type
configuration:configuration
URL:url
options:options
error:&error]) {
// Handle the error
}
}
}
}];
}
最後に、ファイルが変更されたことをオペレーティング システムが認識すると、通知が発生することに注意してください。ファイルがアプリケーション内からロック/ロック解除されると、より迅速に通知を受け取ることができます。
これら 2 つのメソッドをオーバーライドして、変更に対する応答を少し速くすることができます...
- (void)lockWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler {
[super lockWithCompletionHandler:^(NSError * _Nullable error) {
if (completionHandler) completionHandler(error);
if (!error) [self ensureReadOnlyConsistency];
}];
}
- (void)unlockWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler {
[super unlockWithCompletionHandler:^(NSError * _Nullable error) {
if (completionHandler) completionHandler(error);
if (!error) [self ensureReadOnlyConsistency];
}];
}
それがあなたが探しているものであることを願っています。