7

NSFilePresenter メソッドと NSFileCoordinator メソッドを使用してファイルの変更を監視する単純な (おもちゃの) プログラムを作成しようとしています。

このプログラムは、(ハードコードされた) テキスト ファイルをロードするテキスト ビューと、ファイルに変更を加えて保存するボタンで構成されます。アイデアは、2 つのインスタンスを実行していて、1 つのインスタンスに保存すると、他のインスタンスが変更されたファイルをリロードするというものです。

ファイルの読み込みと保存は正常に機能しますが、NSFilePresenter メソッドは呼び出されません。これはすべて、NSFilePresenter プロトコルを実装する FileManager というクラスに基づいています。コードは次のとおりです。

インターフェース:

@interface FileManager : NSObject <NSFilePresenter>
@property (unsafe_unretained) IBOutlet NSTextView *textView;

- (void) saveFile;
- (void) reloadFile;

@end

実装:

@implementation FileManager
{
    NSOperationQueue* queue;
    NSURL* fileURL;
}

- (id) init {
    self = [super init];
    if (self) {
        self->queue = [NSOperationQueue new];
        self->fileURL = [NSURL URLWithString:@"/Users/Jonathan/file.txt"];
        [NSFileCoordinator addFilePresenter:self];
    }
    return self;
}

- (NSURL*) presentedItemURL {
    NSLog(@"presentedItemURL");
    return self->fileURL;
}

- (NSOperationQueue*) presentedItemOperationQueue {
    NSLog(@"presentedItemOperationQueue");
    return self->queue;
}

- (void) saveFile {
    NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
    NSError* error;
    [coordinator coordinateWritingItemAtURL:self->fileURL options:NSFileCoordinatorWritingForMerging error:&error byAccessor:^(NSURL* url) {
        NSString* content = [self.textView string];
        [content writeToFile:[url path] atomically:YES encoding:NSUTF8StringEncoding error:NULL];
    }];
}

- (void) reloadFile {
    NSFileManager* fileManager = [NSFileManager defaultManager];
    NSFileCoordinator* coordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
    NSError* error;
    __block NSData* content;
    [coordinator coordinateReadingItemAtURL:self->fileURL options:0 error:&error byAccessor:^(NSURL* url) {
        if ([fileManager fileExistsAtPath:[url path]]) {
            content = [fileManager contentsAtPath:[url path]];
        }
    }];
    dispatch_async(dispatch_get_main_queue(), ^{
        [self.textView setString:[[NSString alloc] initWithData:content encoding:NSUTF8StringEncoding]];
    });
}

// After this I implement *every* method in the NSFilePresenter protocol. Each one
// simply logs its method name (so I can see it has been called) and calls reloadFile
// (not the correct implementation for all of them I know, but good enough for now).

@end

reloadFile が呼び出されapplicationDidFinishLaunching、保存ボタンがクリックされるたびに saveFile が呼び出されることに注意してください (アプリ デリゲートを介して)。

これまでに呼び出される唯一の NSFilePresenter メソッド (ログによる) はpresentedItemURL です (プログラムが起動してファイルをロードするときに 4 回呼び出され、保存がクリックされるたびに 3 回呼び出されます。2 番目のインスタンスで [保存] をクリックしても、最初のインスタンス。

ここで私が間違っていることを誰かに教えてもらえますか?

4

2 に答える 2

2

(これは古い質問だと思いますが... :))

まず第一に、[NSFileCoordinator removeFilePresenter:self];どこにもないことに気付きました (それは にあるはずですdealloc)。

第二に、あなたは書いた:

    // After this I implement *every* method in the NSFilePresenter protocol. Each one
    // simply logs its method name (so I can see it has been called) and calls reloadFile
    // (not the correct implementation for all of them I know, but good enough for now).

その通りです。これは正しくない実装です。そして、あなたは間違っています:accommodatePresentedItemDeletionWithCompletionHandler:完了ブロックをパラメーターとして受け取るようなメソッドでは、実装するたびにこの完了ブロックを実際に呼び出すことが不可欠であるため、十分ではありません。

    - (void) savePresentedItemChangesWithCompletionHandler:(void (^)(NSError * _Nullable))completionHandler
    {
        // implement your save routine here, but only if you need to!
        if ( dataHasChanged ) [self save]; // <-- meta code
        //
        NSError * err = nil; // <-- = no error, in this simple implementation
        completionHandler(err); // <-- essential!
    }

これがあなたのプロトコル メソッドが呼び出されない理由かどうかはわかりませんが、これは確かに開始する場所です。過去 3 年間で何が悪かったのか、まだ解決していないと仮定してください。:-)

于 2016-06-15T20:29:51.383 に答える