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 in presentedItemDidChange
, you normally need to determine what was changed at first.