我使用的是 Objective-C、Xcode 11、macOS 而不是 iOS、沙盒应用程序。
我需要手动更新最近的文档菜单。 我将 URL 存储在书签中,以便我可以根据沙箱访问它们。
奇怪的是代码 A 可以工作,但 B 却不能。 有人对此有解释吗?
// Code A
NSURL* bookmarkURL = (some valid URL from bookmark);
[bookmarkURL startAccessingSecurityScopedResource]; <- returns TRUE
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:bookmarkURL];
//[bookmarkURL stopAccessingSecurityScopedResource]; <-- Without closing access it works
// Code B
NSURL* bookmarkURL = (some valid URL from bookmark);
[bookmarkURL startAccessingSecurityScopedResource]; <- returns TRUE
[[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:bookmarkURL];
[bookmarkURL stopAccessingSecurityScopedResource];
B 不起作用(关闭安全访问)!不关闭安全访问感觉是错误的。 对于情况 B,会引发以下错误
Insert failed for list identifier com.apple.LSSharedFileList.ApplicationRecentDocuments Error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" (Restricted by sandbox) UserInfo={NSDebugDescription=Restricted by sandbox}
如果不启动安全访问,就会抛出相同的错误(显然)。
最佳答案
看来,只要文档位于(手动管理的)最近列表中,应用程序就应该保留安全范围的权限。
就我而言,错误表现在
- “文件/打开最近的文件”中的“清除菜单”菜单项保持灰色
- 最近的列表在应用启动后并未持续存在
- 最近的列表未出现在 Dock 上。
- 我在 Console.app 中遇到错误:
Insert failed for list identifier com.apple.LSSharedFileList.ApplicationRecentDocuments Error: Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" (Access to list denied) UserInfo={NSDebugDescription=Access to list denied}
似乎最近的文档列表在内部是由一个单独的线程管理的,该线程又与系统进程sharedfilelistd
进行通信。如果我 stopAccessingSecurityScopedResource
,我可以通过以下调用堆栈(缩写)在 Console.app 中观察到 sandboxd
违规:
Sandbox: MyApp(24093) deny(1) file-read-data /path/to/recent/document
Thread 1 (id: 364320):
0 libsystem_kernel.dylib 0x00007ff81b2265b2 mach_msg2_trap + 10
1 libsystem_kernel.dylib 0x00007ff81b22d5e4 mach_msg_overwrite + 692
2 libsystem_kernel.dylib 0x00007ff81b22689a mach_msg + 19
3 libdispatch.dylib 0x00007ff81b0dce6f _dispatch_mach_send_and_wait_for_reply + 518
4 libdispatch.dylib 0x00007ff81b0dd273 dispatch_mach_send_with_result_and_wait_for_reply + 50
5 libxpc.dylib 0x00007ff81afacb97 xpc_connection_send_message_with_reply_sync + 238
6 Foundation 0x00007ff81c18b423 __NSXPCCONNECTION_IS_WAITING_FOR_A_SYNCHRONOUS_REPLY__ + 9
7 Foundation 0x00007ff81c1893c1 -[NSXPCConnection _sendInvocation:orArguments:count:methodSignature:selector:withProxy:] + 3215
8 Foundation 0x00007ff81c188723 -[NSXPCConnection _sendInvocation:withProxy:] + 91
9 CoreFoundation 0x00007ff81b3234c6 ___forwarding___ + 671
10 CoreFoundation 0x00007ff81b323198 _CF_forwarding_prep_0 + 120
11 SharedFileList 0x00007ff823244145 __44-[SFLGenericList _insertItem:atIndex:error:]_block_invoke + 695
12 libsystem_trace.dylib 0x00007ff81afeaa0e _os_activity_initiate_impl + 51
13 SharedFileList 0x00007ff823243ddc -[SFLGenericList _insertItem:atIndex:error:] + 301
14 SharedFileList 0x00007ff823243ba5 __46-[SFLGenericList _insertItem:afterItem:error:]_block_invoke + 378
15 libsystem_trace.dylib 0x00007ff81afeaa0e _os_activity_initiate_impl + 51
16 SharedFileList 0x00007ff823243961 -[SFLGenericList _insertItem:afterItem:error:] + 316
17 SharedFileList 0x00007ff8232433b2 -[SFLGenericList insertItem:afterItem:error:] + 100
18 SharedFileList 0x00007ff82323c085 +[SFLList(LSSharedFileListSupport) itemByInsertingAfterItem:name:URL:propertiesToSet:propertiesToClear:list:] + 1370
19 SharedFileList 0x00007ff82323f3f7 LSSharedFileListInsertItemURL + 183
20 AppKit 0x00007ff81e80868f -[_NSRecentItemsMenuController _notePendingRecentDocumentURLsForKey:documentsSnapshot:] + 677
21 Foundation 0x00007ff81c1ada71 __NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK__ + 7
22 Foundation 0x00007ff81c1ad969 -[NSBlockOperation main] + 98
23 Foundation 0x00007ff81c1ad902 __NSOPERATION_IS_INVOKING_MAIN__ + 17
24 Foundation 0x00007ff81c1acc02 -[NSOperation start] + 782
25 Foundation 0x00007ff81c1ac8e8 __NSOPERATIONQUEUE_IS_STARTING_AN_OPERATION__ + 17
26 Foundation 0x00007ff81c1ac7b6 __NSOQSchedule_f + 182
27 libdispatch.dylib 0x00007ff81b0d1771 _dispatch_block_async_invoke2 + 83
...
这表明系统启动了一个线程,执行一些名为 [_NSRecentItemsMenuController _notePendingRecentDocumentURLs...]
的异步分派(dispatch)操作,该操作又向 SFL(共享文件列表)发出 XPC 调用),这会以某种方式导致尝试读取指定文档,该文档被应用程序沙箱阻止。
因此我们可以得出结论,在调用 noteNewRecentDocumentURL
后,应用程序仍然必须持有访问该文件的权限,因为最近列表的实际管理是异步发生在另一个线程上的,并且该线程必须能够检查文件是否存在或出于其他原因读取文件。
我重写了我的应用程序,以便只要关联的文件位于最近的列表中,它就保留安全范围的资源,并在它们从列表中删除时停止访问它们。互联网智慧称有几千个此类资源可用,请参阅What are the current kernel resource limits on security-scoped bookmarks? 。由于我最近的列表永远不会增长到这样的大小,因此我的应用程序永远不会耗尽内核资源。
关于objective-c - NSDocumentController 最近的文档沙盒(奇怪)问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59442880/