0

サブクラスを使用してロックされたファイルを開くNSPersistentDocumentと、コンソールに次のメッセージが表示されます。

読み取り/書き込み可能なパス [URL] に読み取り専用ファイルを追加しようとしています。代わりに読み取り専用を追加します。これは、将来的には重大なエラーになります。NSReadOnlyPersistentStoreOption を指定する必要があります。

ドキュメント ウィンドウのタイトルは「(ドキュメント名) - ロック済み」です。ユーザーがロックを解除し、変更を加えてから保存しようとすると、エラーで保存に失敗します

保存中にエラーが発生しました。

NSPersistentDocument は、ユーザーがドキュメントのロックを解除したことを認識できず、読み取り/書き込みモードでドキュメントを再度開かないようです。これはバグですか、NSPersistentDocumentそれとも何か不足していますか?

のファイル I/O メソッドをオーバーライドしていませんNSPersistentDocument

4

1 に答える 1

1

ああ、自動ファイル ロックです。

これは、しばらくアクセスされていない自動保存ドキュメントで発生します。

典型的なアプローチは、コア データ スタックを作成する前にロックに気づき、ユーザーにファイルのロックを解除するように求めるダイアログを表示することです。

彼らがファイルのロックを解除することに同意した場合は、単にロックを解除して通常どおり実行します。

ロックを解除することに同意しない場合は、コピーするか、読み取り専用で開きます。もちろん、単純にユーザーの設定をバイパスしてファイルのロックを自動的に解除することもできますが、それはおそらくあまり良い方法ではありません。

ファイルがロックされているかどうかを判断し、ファイルをロック/ロック解除するのに役立つカテゴリを次に示します。

これは、ファイルモードが読み取り専用に変更されることとは完全に別のものですが、同様の方法で処理できることに注意してください。

カテゴリ インターフェイス

@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];
    }];
}

それがあなたが探しているものであることを願っています。

于 2015-10-27T20:53:23.980 に答える