I'm working on an NSDocument
subclass. It represents a text file in a text editor.
I'm trying to use the NSFilePresenter
protocol to respond to changes made by other applications (i.e., if the user saves a change in TextEdit while the same file is open here).
My current implementation works like this…
I add a property for a file coordinator:
@property (nonatomic) NSFileCoordinator *fileCoordinator;
I create it lazily:
- (NSFileCoordinator *) fileCoordinator {
if (!_fileCoordinator) {
[NSFileCoordinator addFilePresenter:self];
_fileCoordinator = [[NSFileCoordinator alloc] initWithFilePresenter:self];
}
return _fileCoordinator;
}
When presentedItemDidChange
is called, I reload the file from disk and display it:
- (void)presentedItemDidChange {
[super presentedItemDidChange];
NSLog(@"presentedItemDidChange was called");
if (self.presentedItemURL.isFileURL && self.fileType) {
NSError *coordinatorError = nil;
[self.fileCoordinator coordinateReadingItemAtURL:self.presentedItemURL options:NSFileCoordinatorReadingWithoutChanges error:&coordinatorError byAccessor:^(NSURL *newURL) {
NSError *readError = nil;
[self readFromURL:newURL ofType:self.fileType error:&readError];
if (readError) NSLog(@"%@", readError);
}];
if (coordinatorError) NSLog(@"%@", coordinatorError);
[self reloadString];
}
}
This code works: when I save a file in TextEdit, the changes appear in my app.
However, once I save this file, presentedItemDidChange
is called repeatedly (about once per second). After a few minutes, the app crashes due to a memory error. No errors are logged; the console looks basically like this:
2016-02-17 22:43:46.233 MacDown[66847:2470964] presentedItemDidChange was called
2016-02-17 22:43:51.721 MacDown[66847:2470960] presentedItemDidChange was called
2016-02-17 22:43:52.816 MacDown[66847:2471206] presentedItemDidChange was called
2016-02-17 22:43:53.819 MacDown[66847:2470964] presentedItemDidChange was called
2016-02-17 22:43:54.920 MacDown[66847:2471206] presentedItemDidChange was called
2016-02-17 22:43:56.014 MacDown[66847:2470964] presentedItemDidChange was called
2016-02-17 22:43:57.115 MacDown[66847:2471206] presentedItemDidChange was called
2016-02-17 22:43:58.117 MacDown[66847:2470964] presentedItemDidChange was called
This is my first time using these APIs, so I assume I'm making a simple mistake. If it matters, I'm running OS X 10.11.3 and Xcode 7.2. What am I doing wrong?
presentedItemDidChange
will be called not only when the file contents was changed but also when the metadata of the file was changed. And[NSDocument -readFromData:ofType:error:]
overwrite the last opened date, one of the file metadata, of the file.So, this is the reason why
presentedItemDidChange
was called repeatedly. If you want to handle updated files inpresentedItemDidChange
, you normally need to determine what was changed at first.cf. NSFilePresenter Protocol Reference