【问题标题】:MDQueryGetResultAtIndex and UnsafeRawPointer in Swift 3Swift 3 中的 MDQueryGetResultAtIndex 和 UnsafeRawPointer
【发布时间】:2017-09-14 16:24:48
【问题描述】:

我无法使用 MDQuery 在 Swift 3 中探索 Spotlight 搜索的结果。我希望 MDQueryGetResultAtIndex 产生一个 MDItem,并且在 C/Objective-C 中,该假设有效,我可以调用 MDItemCopyAttribute 来探索该项目。在这里,例如,我成功获取了一个找到的项目的路径名:

MDItemRef item = (MDItemRef)MDQueryGetResultAtIndex(q,i);
CFStringRef path = MDItemCopyAttribute(item,kMDItemPath);

但在 Swift 3 中,MDQueryGetResultAtIndex 返回一个 UnsafeRawPointer!(它是 C 中的指向 void 的指针)。为了克服这一点,我尝试过,例如:

if let item = MDQueryGetResultAtIndex(q, 0) {
    let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
    let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}

但是崩溃了,并且日志显示ptr.pointee 是一个 NSAtom。很明显,我个人的 UnsafeRawPointer mojo 无法正常工作(坦率地说,我一直觉得这很令人困惑)。

如何将此 UnsafeRawPointer 转换为可以成功调用 MDItemCopyAttribute 的东西?

替代品

  • 我可以通过将我的 Objective-C 代码放入一个 Objective-C 帮助对象并从 Swift 调用它来克服这个困难;但我想写一个纯 Swift 的解决方案。

  • 同样,我可能会重写我的代码以使用更高级别的 NSMetadataQuery,而且我可能会这样做;但是我使用较低级别的 MDQueryRef 的原始 Objective-C 代码工作正常,所以现在我很好奇如何将它直接变成 Swift。


完整代码适合那些想在家尝试的人:

let s = "kMDItemDisplayName == \"test\"" // you probably have one somewhere
let q = MDQueryCreate(nil, s as CFString, nil, nil)
MDQueryExecute(q, CFOptionFlags(kMDQuerySynchronous.rawValue))
let ct = MDQueryGetResultCount(q)
if ct > 0 {
    if let item = MDQueryGetResultAtIndex(q, 0) {
        // ... 
    }
}

【问题讨论】:

  • 愚蠢的问题,item?.assumingMemoryBound(to: MDItem.self)?.pointee 获取 MDItem 怎么样?
  • @JAL 结果与bindMemory 相同。我也尝试将它们一起使用,以防万一。 :)
  • 嗯,不错,值得一试。我猜你为什么得到NSAtom是因为你的ptr变成UnsafePointer(0x1),得到pointee相当于调用(id)0x1之类的东西__NSAtom .我会继续挖掘。
  • 我会假设你必须通过Unmanaged<MDItem>.fromOpaque(...)——但我不知道MDQuery是如何工作的,所以无法测试它。
  • @Hamish 我会试试的!当然,我也可以发布更多代码。等一下。

标签: swift macos spotlight


【解决方案1】:

代码中的问题

if let item = MDQueryGetResultAtIndex(q, 0) {
    let ptr = item.bindMemory(to: MDItem.self, capacity: 1)
    let path = MDItemCopyAttribute(ptr.pointee, kMDItemPath)
}

UnsafeRawPointer 被解释为一个 指针 MDItem 引用,然后在 ptr.pointee 中取消引用,但是 原始指针 MDItem 引用,所以它被取消引用 一次太频繁了。

将原始指针转换为MDItem 引用的“最短”方法 是unsafeBitCast:

let item = unsafeBitCast(rawPtr, to: MDItem.self)

这是(Objective-)C cast的直接类似物。 您也可以使用Unmanaged 方法 将原始指针转换为非托管引用并从那里 到托管引用(比较 How to cast self to UnsafeMutablePointer<Void> type in swift):

let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()

这看起来有点复杂,但也许表达了意图 更清楚。后一种方法也适用于 (+1) 保留 参考资料(使用takeRetainedValue())。

独立示例:

import CoreServices

let queryString = "kMDItemContentType = com.apple.application-bundle"
if let query = MDQueryCreate(kCFAllocatorDefault, queryString as CFString, nil, nil) {
    MDQueryExecute(query, CFOptionFlags(kMDQuerySynchronous.rawValue))

    for i in 0..<MDQueryGetResultCount(query) {
        if let rawPtr = MDQueryGetResultAtIndex(query, i) {
            let item = Unmanaged<MDItem>.fromOpaque(rawPtr).takeUnretainedValue()
            if let path = MDItemCopyAttribute(item, kMDItemPath) as? String {
                print(path)
            }
        }
    }
}

【讨论】:

  • "UnsafeRawPointer 被解释为指向 MDItem 引用的 指针,然后在 ptr.pointee 中取消引用,但原始指针 MDItem参考”这是关于何时需要fromOpaque 舞蹈的关键事实,我从未理解过。正如 JAL 在上面的评论中所暗示的那样,CFType 的桥接方式让我感到困惑。
  • 很好的回答马丁和很好的问题马特。听起来这个 API 是一个需要更新 Swift 的完美示例。
  • 我想我看到了@JAL 在这里得到了什么。指向 void / UnsafeRawPointer 的指针背后的通常假设是,我们已经抛弃了指针 在远端 的类型。但是当在这里插入 CFTypeRef 时,正如 Martin R 所说,它指针。这是我直到现在才意识到的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-19
  • 2017-03-30
  • 1970-01-01
  • 1970-01-01
  • 2019-02-17
相关资源
最近更新 更多