我正在尝试编写一个简单的(玩具)程序,它使用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没有明显对一审的影响。
谁能告诉我这里我做错了什么?
答案 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!
}
我不知道这是否是您的协议方法未被调用的原因,但它确实是一个开始的地方。好吧,假设你已经找不到过去三年的错误了! : - )