NSFilePresenter 方法永远不会被调用
Posted
技术标签:
【中文标题】NSFilePresenter 方法永远不会被调用【英文标题】:NSFilePresenter methods never get called 【发布时间】:2013-03-27 22:26:49 【问题描述】:我正在尝试编写一个简单的(玩具)程序,它使用 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(通过应用程序委托)。
唯一被调用的 NSFilePresenter 方法(根据日志)是presentItemURL(程序启动并加载文件时调用四次,单击保存时调用三次。在第二个实例中单击保存没有明显对一审的影响。
谁能告诉我我在这里做错了什么?
【问题讨论】:
【参考方案1】:我为这个确切的问题苦苦挣扎了很长一段时间。对我来说,唯一会被调用的方法是-presentedSubitemDidChangeAtURL:
(我监控的是一个目录而不是一个文件)。我向 Apple 提出了技术支持问题,他们的回应是这是一个错误,我们现在唯一能做的就是通过 -presentedSubitemDidChangeAtURL:
完成所有操作,如果您正在监视目录。不知道监控文件时可以做什么。
我鼓励任何遇到此问题的人提交错误 (https://bugreport.apple.com) 以鼓励 Apple 尽快解决此问题。
【讨论】:
【参考方案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!
我不知道这是否是您的协议方法没有被调用的原因,但这肯定是一个开始的地方。好吧,假设您还没有弄清楚过去三年中出了什么问题! :-)
【讨论】:
"它应该在 dealloc" 对吗?那不是循环引用的情况吗?在向 NSFileCoordinator 注册事物时不会调用 dealloc? "虽然你可以在它的初始化方法中注册一个文件展示器,但你不能在它的释放方法中取消注册一个文件展示器。因为 NSFileCoordinator 保留了注册的文件展示器对象,系统不会释放文件演示者,直到您取消注册它,即使您自己的代码不再保留它。" (ref)以上是关于NSFilePresenter 方法永远不会被调用的主要内容,如果未能解决你的问题,请参考以下文章
SwiftUI:PreferenceKey Reduce 方法永远不会被调用
iOS 8 自定义标签 LayoutSubviews 方法永远不会被调用?
UITabBarController 委托方法永远不会被调用
PictureBox 中的 DragDrop - DragEnter 永远不会被调用