【问题标题】:How to get a file reference URL in Swift?如何在 Swift 中获取文件引用 URL?
【发布时间】:2017-02-10 21:09:03
【问题描述】:

我无法获得在 Swift 中工作的文件引用 URL。首先,Swift 的 URL 类型甚至没有定义将文件路径 URL 转换为文件引用 URL 的方法。 NSURL 有这样的方法:fileReferenceURL() -> URL?,但我不知道如何让它工作。

我尝试了什么:

let myURL = URL(string: "file:///Users/Me/Desktop/CoolFolder")!
let refURL = (myURL as NSURL).fileReferenceURL()!
NSLog("\(myURL)")   // file:///Users/Me/Desktop/CoolFolder (ok)
NSLog("\(refURL)")  // file:///Users/Me/Desktop/CoolFolder (wha?!?)
logFolderContents() // Logs contents of folder pointed to by refURL
// ...
// Rename CoolFolder -> UncoolFolder in Finder.
// ...
logFolderContents() // Fails!
// ...
// Rename UncoolFolder -> CoolFolder.
// ...
logFolderContents() // Successfully logs contents again.

基本上,refURL 继续指向原始文件路径。对fileReferenceURL() 的调用似乎没有任何作用。

如何在 Swift 中获取文件引用 URL?

更新: 用户@Sam 在 cmets 中指出,这似乎是 Swift 3 回归错误,如下所述:https://bugs.swift.org/browse/SR-2728。请访问链接并对此错误进行投票。该线程还包含一个解决方法。

【问题讨论】:

  • 您期望refURL 具有什么价值?
  • 我希望它看起来像这样:file:///.file/id=6571367.2773272/。文件引用 URL 不包含路径名,而是一个唯一的 ID。当路径名称更改(资源被重命名或移动)时,ID 仍然指向相同的资源。见developer.apple.com/library/prerelease/content/documentation/…
  • 作为测试,将第一行更改为:let myURL = URL(fileURLWithPath: "/Users/Me/Desktop/CoolFolder")
  • @rmaddy 结果相同。
  • 看起来这是一个已知的错误:bugs.swift.org/browse/SR-2728

标签: swift cocoa filesystems nsurl


【解决方案1】:

遗憾的是,截至 2021 年 4 月,该错误仍未修复。这使得比较文件 URL 的相等性非常困难。

使用 ObjC 效果很好:

NSString *p1 = @"/etc/hosts";
NSString *p2 = @"/private/etc/hosts";
    
NSURL *url1 = [NSURL fileURLWithPath:p1];
NSURL *url2 = [NSURL fileURLWithPath:p2];
    
NSURL *ref1 = url1.fileReferenceURL;
NSURL *ref2 = url2.fileReferenceURL;
    
BOOL equal = [ref1 isEqual:ref2];

在 Swift 中,我使用以下代码得到了几乎相同的结果(即包含卷 ID 和文件 ID 的 64 位):

let p1 = "/etc/hosts"
let p2 = "/private/etc/hosts"

let url1 = URL(fileURLWithPath: p1)
let url2 = URL(fileURLWithPath: p2)

// This does not work because the refs do not use fileIDs as they should
// See https://bugs.swift.org/browse/SR-2728
let ref1bad = (url1 as NSURL).fileReferenceURL()!
let ref2bad = (url2 as NSURL).fileReferenceURL()!
let equalBad = ref1bad == ref2bad
print("\(ref1bad) = \(ref2bad): \(equalBad)")

// This works:
var ref1: NSObject?
var ref2: NSObject?
if let resourceValues = try? url1.resourceValues(forKeys: [.fileResourceIdentifierKey]) {
    ref1 = resourceValues.fileResourceIdentifier as? NSObject
}
if let resourceValues = try? url2.resourceValues(forKeys: [.fileResourceIdentifierKey]) {
    ref2 = resourceValues.fileResourceIdentifier as? NSObject
}
let equal = ref1!.isEqual(ref2) // the use of "!" is probably not good here
print("\(String(describing: ref1)) = \(String(describing: ref2)): \(equal)")

如果你想要一个真实的文件引用 URL,你必须从对象中的两个 32 位值(它似乎是 NSData 类型)重建 URL,并使用第一个作为文件 ID,第二个作为volumeID。不过,为此编写代码超出了我目前相当微薄的 Swift 技能。

【讨论】:

  • 感谢您跟进这个老问题。不久前我基本上放弃了 Swift,部分原因是这个问题。成熟后的某一天可能会重新访问。支持 Obj-C。
猜你喜欢
  • 2020-11-01
  • 2017-10-03
  • 2021-10-15
  • 2017-01-22
  • 2014-03-17
  • 1970-01-01
  • 2017-11-16
  • 2011-02-08
  • 1970-01-01
相关资源
最近更新 更多