Swift 3中的MDQueryGetResultAtIndex和UnsafeRawPointer

时间:2017-04-18 17:36:49

标签: swift macos spotlight

我无法使用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中指向空的指针)。为了解决这个问题,我尝试过,例如:

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) {
        // ... 
    }
}

1 个答案:

答案 0 :(得分:3)

代码中的问题

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)
            }
        }
    }
}