在 macOS 的 SwiftUI 应用程序中,我尝试在默认钥匙串中保留密码。对于可以说是笨重的 Core Foundation API,并不缺乏示例代码和包装器。它们本质上都使用相同的模式从钥匙串中检索密码 - 某种程度上像这样:
func getPassword(forService service: String) throws -> String? {
let query = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecMatchLimit: kSecMatchLimitOne,
kSecReturnData: true
] as NSDictionary
var result: CFTypeRef?
let status = SecItemCopyMatching(query, &result)
if status == errSecItemNotFound {
return nil
}
if status != errSecSuccess {
throw NSError(domain: NSOSStatusErrorDomain, code: Int(status))
}
guard let data = result as? Data else { return nil }
return String(data: data, encoding: .utf8)
}
上面的代码可以工作,但每次调用时也会泄漏内存。泄漏并未在 Instruments 中显示,但当您循环上述函数时,泄漏会很明显。我想线索就在函数的名称中
SecItemCopyMatching
。它复制结果,但结果不会被释放。
这个问题已被记录在案,例如在苹果论坛这里,但我似乎找不到任何解决方案。
我尝试按照这个答案中建议的方式手动释放内存(针对不同的问题),但这会导致崩溃。
关于如何在 Swift 中正确使用
SecItemCopyMatching
有什么想法吗?
仪器中未显示泄漏,但当您循环上面的函数时,泄漏很明显。
如果您在循环中运行它,则很可能需要其中的 autorelease pool 来释放循环中生成的自动释放内存。这通常不是必需的,因为自动释放池将在事件循环结束时自动耗尽。但是,如果您有一个生成大量自动释放对象的内部循环,您可能希望尽快耗尽它。
这通常看起来像:
for x in ... {
@autoreleasepool {
thingThatGeneratesAutoreleasedObjects()
}
}
虽然 SecItemCopyMatching 确实复制值,但它们被 ARC 释放。如果您手动管理内存,则需要添加对 CFRelease 的调用,但这在 ARC(Swift 使用的)下不是必需的。