是否有 NSFileCoordinator 的替代方法用于在沙箱中打开相关文件?
Is there an alternative to NSFileCoordinator for opening related files in a sandbox?
这是 Access sidecar files in a Mac sandboxed app 的后续。
虽然那里的答案没有涵盖,Apple 文档告诉我们要访问 "related file" 我们必须使用 NSFileCoordinator
来访问 (ref)。
这对我的需求来说有点繁重,并且会带来架构问题,因为实际的文件访问是在我的后端代码中进行的,远离 Apple 库设施的范围。如果可以的话,我不想使用 NSFileCoordinator
来获取相关文件的内容。我也不想要求我的用户手动识别 sidecar 文件(如果不出意外,这对于批处理来说是一个糟糕的工作流程)。我只想告诉沙盒"this is okay, this app can open such-and-such a related File.XYZ after the user chose File.ABC".
对于正常的文件访问,这不是问题:使用 std::ifstream
到 open a file that's been previously selected from an Open panel 似乎在应用程序实例的剩余生命周期内有效。
不过开个"related file"好像比较受限
在我的应用程序的 plist 中添加了一个 NSIsRelatedItemType
(如链接的答案中所示),我在前端可以做的最少的事情是什么,大概是在打开 "primary" 之后/requested 文件,这样我以后也可以使用 std::ifstream
打开相关的 sidecar 文件?关于这个主题的文档似乎有点稀疏……
也许我最好的选择是执行一次性提示,让用户授权访问封装目录,并将生成的授权保存为应用程序范围的书签 (),但同样不是这样和我想要的一样透明。用户遇到这样的请求也可能有点 "scary"。
不,因为 OS 会 [可能] 实际上将文件复制到不同的位置,以便您可以访问它,所以您必须使用 NSFileCoordinator
.
但并非一无所有!有一个 hack:即使你的后端代码被设计成可移植的,如果你将文件读取 .cpp
设置为 Xcode 中的 "Objective-C++ Source",你可以使用 Foundation 功能( #import <Foundation/Foundation.h>
) 就在那里。
因此,无论您当前在何处实例化和读取 std::ifstream
,都有一个 #if defined(PLATFORM_MAC_OS_X)
(或其他),并且在其中用 NSFileCoordinator
代码包装您的文件读取.
最上面:
#ifdef PLATFORM_MAC_OS_X
#import <Foundation/Foundation.h>
@interface SidecarPresenter : NSObject<NSFilePresenter>
@property(readwrite, copy) NSURL* presentedItemURL;
@property(readwrite, copy) NSURL* primaryPresentedItemURL;
@property(readwrite, assign) NSOperationQueue* presentedItemOperationQueue;
-(instancetype)initWithImageUrl:(NSURL*)imageUrl andSidecarExtension:(NSString*)newExt;
@end
@implementation SidecarPresenter
- (instancetype)initWithImageUrl:(NSURL*)imageUrl andSidecarExtension:(NSString*)newExt
{
self = [super init];
if (self)
{
[self setPrimaryPresentedItemURL:imageURL];
[self setPresentedItemURL:[[imageUrl URLByDeletingPathExtension] URLByAppendingPathExtension:newExt]];
[self setPresentedItemOperationQueue:[NSOperationQueue mainQueue]];
}
return self;
}
- (void)dealloc
{
[_primaryPresentedItemURL release];
[_presentedItemURL release];
[super dealloc];
}
@end
#endif
以后:
#ifdef PLATFORM_MAC_OS_X
SidecarPresenter* presenter = [SidecarPresenter alloc];
[presenter initWithImageUrl:[NSURL fileURLWithPath:documentFilename]
andSidecarExtension:sidecarExtension]];
[presenter autorelease];
[NSFileCoordinator addFilePresenter:presenter];
NSFileCoordinator* coordinator = [[[NSFileCoordinator alloc] initWithFilePresenter:presenter] autorelease];
NSError* error = nil;
[coordinator coordinateReadingItemAtURL:presenter.presentedItemURL
options:NSFileCoordinatorReadingWithoutChanges
error:&error
byAccessor:^(NSURL* newURL)
{
std::ifstream strm([newURL fileSystemRepresentation]);
foo(strm);
}];
[NSFileCoordinator removeFilePresenter:presenter];
#else
std::ifstream strm(documentFilename);
foo(strm);
#endif
这样,后端和前端之间就没有来回的乒乓球了。而且 lambda 是同步调用的,因此您也不必担心竞争条件(可能只是一点额外的延迟)。唯一的代价是一些特定于平台的泄漏,但至少它隐藏在预处理器指令中。
这是 Access sidecar files in a Mac sandboxed app 的后续。
虽然那里的答案没有涵盖,Apple 文档告诉我们要访问 "related file" 我们必须使用 NSFileCoordinator
来访问 (ref)。
这对我的需求来说有点繁重,并且会带来架构问题,因为实际的文件访问是在我的后端代码中进行的,远离 Apple 库设施的范围。如果可以的话,我不想使用 NSFileCoordinator
来获取相关文件的内容。我也不想要求我的用户手动识别 sidecar 文件(如果不出意外,这对于批处理来说是一个糟糕的工作流程)。我只想告诉沙盒"this is okay, this app can open such-and-such a related File.XYZ after the user chose File.ABC".
对于正常的文件访问,这不是问题:使用 std::ifstream
到 open a file that's been previously selected from an Open panel 似乎在应用程序实例的剩余生命周期内有效。
不过开个"related file"好像比较受限
在我的应用程序的 plist 中添加了一个 NSIsRelatedItemType
(如链接的答案中所示),我在前端可以做的最少的事情是什么,大概是在打开 "primary" 之后/requested 文件,这样我以后也可以使用 std::ifstream
打开相关的 sidecar 文件?关于这个主题的文档似乎有点稀疏……
也许我最好的选择是执行一次性提示,让用户授权访问封装目录,并将生成的授权保存为应用程序范围的书签 (
不,因为 OS 会 [可能] 实际上将文件复制到不同的位置,以便您可以访问它,所以您必须使用 NSFileCoordinator
.
但并非一无所有!有一个 hack:即使你的后端代码被设计成可移植的,如果你将文件读取 .cpp
设置为 Xcode 中的 "Objective-C++ Source",你可以使用 Foundation 功能( #import <Foundation/Foundation.h>
) 就在那里。
因此,无论您当前在何处实例化和读取 std::ifstream
,都有一个 #if defined(PLATFORM_MAC_OS_X)
(或其他),并且在其中用 NSFileCoordinator
代码包装您的文件读取.
最上面:
#ifdef PLATFORM_MAC_OS_X
#import <Foundation/Foundation.h>
@interface SidecarPresenter : NSObject<NSFilePresenter>
@property(readwrite, copy) NSURL* presentedItemURL;
@property(readwrite, copy) NSURL* primaryPresentedItemURL;
@property(readwrite, assign) NSOperationQueue* presentedItemOperationQueue;
-(instancetype)initWithImageUrl:(NSURL*)imageUrl andSidecarExtension:(NSString*)newExt;
@end
@implementation SidecarPresenter
- (instancetype)initWithImageUrl:(NSURL*)imageUrl andSidecarExtension:(NSString*)newExt
{
self = [super init];
if (self)
{
[self setPrimaryPresentedItemURL:imageURL];
[self setPresentedItemURL:[[imageUrl URLByDeletingPathExtension] URLByAppendingPathExtension:newExt]];
[self setPresentedItemOperationQueue:[NSOperationQueue mainQueue]];
}
return self;
}
- (void)dealloc
{
[_primaryPresentedItemURL release];
[_presentedItemURL release];
[super dealloc];
}
@end
#endif
以后:
#ifdef PLATFORM_MAC_OS_X
SidecarPresenter* presenter = [SidecarPresenter alloc];
[presenter initWithImageUrl:[NSURL fileURLWithPath:documentFilename]
andSidecarExtension:sidecarExtension]];
[presenter autorelease];
[NSFileCoordinator addFilePresenter:presenter];
NSFileCoordinator* coordinator = [[[NSFileCoordinator alloc] initWithFilePresenter:presenter] autorelease];
NSError* error = nil;
[coordinator coordinateReadingItemAtURL:presenter.presentedItemURL
options:NSFileCoordinatorReadingWithoutChanges
error:&error
byAccessor:^(NSURL* newURL)
{
std::ifstream strm([newURL fileSystemRepresentation]);
foo(strm);
}];
[NSFileCoordinator removeFilePresenter:presenter];
#else
std::ifstream strm(documentFilename);
foo(strm);
#endif
这样,后端和前端之间就没有来回的乒乓球了。而且 lambda 是同步调用的,因此您也不必担心竞争条件(可能只是一点额外的延迟)。唯一的代价是一些特定于平台的泄漏,但至少它隐藏在预处理器指令中。