永远不会调用NSFilePresenter方法

时间:2013-03-27 22:26:50

标签: objective-c cocoa nsfilecoordinator

我正在尝试编写一个简单的(玩具)程序,它使用NSFilePresenter和NSFileCoordinator方法来监视文件的变化。

该程序包含一个文本视图,用于加载(硬编码)文本文件和一个按钮,该按钮将保存文件并进行任何更改。我的想法是我有两个实例正在运行,在一个实例中保存将导致另一个实例重新加载更改的文件。

加载和保存文件工作正常,但从不调用NSFilePresenter方法。它都基于一个名为FileManager的类,它实现了NSFilePresenter协议。代码如下:

接口:

@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

注意,在applicationDidFinishLaunching中调用reloadFile,每次点击保存按钮时都会调用saveFile(通过app delegate)。

唯一被调用的NSFilePresenter方法(通过日志)是presentItemURL(在程序启动时调用四次并在加载文件时调用四次,单击save时调用三次。在第二次实例中单击save没有明显对一审的影响。

谁能告诉我这里我做错了什么?

2 个答案:

答案 0 :(得分:3)

我正在努力解决这个问题已有一段时间了。对我来说,唯一可以调用的方法是-presentedSubitemDidChangeAtURL:(我正在监视目录而不是文件)。我向Apple提出了一个技术支持问题,他们的回答是这是一个错误,我们现在唯一可以做的就是通过-presentedSubitemDidChangeAtURL:来监控目录。不确定在监控文件时可以做些什么。

我会鼓励任何遇到此问题的人提交错误(https://bugreport.apple.com),以鼓励Apple尽快解决此问题。

答案 1 :(得分: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!
    }

我不知道这是否是您的协议方法未被调用的原因,但它确实是一个开始的地方。好吧,假设你已经找不到过去三年的错误了! : - )