我有一个使用Realm发布的应用程序,并且有一些崩溃日志显示有时无法使用配置创建领域导致EXC_BREAKPOINT(SIGTRAP)崩溃。 (有几百个应用程序安装的9个崩溃文件,所以它不是经常发生的事情)
@objc class Database : NSObject
{
let configuration = Realm.Configuration(encryptionKey: Database.getKey() as Data)
var transactionRealm:Realm? = nil
override init()
{
let realm = try! Realm(configuration: configuration) // Crash here
<snip>
}
// This getKey() method is taken from the Realm website
class func getKey() -> NSData {
let keychainIdentifier = "Realm.EncryptionKey.AppKey"
let keychainIdentifierData = keychainIdentifier.data(using: String.Encoding.utf8, allowLossyConversion: false)!
// First check in the keychain for an existing key
var query: [NSString: AnyObject] = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
kSecAttrKeySizeInBits: 512 as AnyObject,
kSecReturnData: true as AnyObject
]
// To avoid Swift optimization bug, should use withUnsafeMutablePointer() function to retrieve the keychain item
// See also: http://stackoverflow.com/questions/24145838/querying-ios-keychain-using-swift/27721328#27721328
var dataTypeRef: AnyObject?
var status = withUnsafeMutablePointer(to: &dataTypeRef) { SecItemCopyMatching(query as CFDictionary, UnsafeMutablePointer($0)) }
if status == errSecSuccess {
return dataTypeRef as! NSData
}
// No pre-existing key from this application, so generate a new one
let keyData = NSMutableData(length: 64)!
let result = SecRandomCopyBytes(kSecRandomDefault, 64, keyData.mutableBytes.bindMemory(to: UInt8.self, capacity: 64))
assert(result == 0, "REALM - Failed to get random bytes")
// Store the key in the keychain
query = [
kSecClass: kSecClassKey,
kSecAttrApplicationTag: keychainIdentifierData as AnyObject,
kSecAttrKeySizeInBits: 512 as AnyObject,
kSecValueData: keyData,
kSecAttrAccessible: kSecAttrAccessibleAfterFirstUnlock
]
status = SecItemAdd(query as CFDictionary, nil)
assert(status == errSecSuccess, "REALM - Failed to insert the new key in the keychain")
return keyData
}
以下是崩溃文件的相关部分:
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000103c0f30c
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [0]
Triggered by Thread: 0
Thread 0 name:
Thread 0 Crashed:
0 libswiftCore.dylib 0x0000000103c0f30c 0x103ab4000 + 1422092
1 libswiftCore.dylib 0x0000000103c0f30c 0x103ab4000 + 1422092
2 libswiftCore.dylib 0x0000000103b13d2c 0x103ab4000 + 392492
3 libswiftCore.dylib 0x0000000103b13bf4 0x103ab4000 + 392180
4 My app 0x000000010334ff14 _TFC14Caller_Name_ID8DatabasecfT_S0_ + 1648 (Database.swift:21)
5 My app 0x0000000103330624 -[Model createDatabase] + 200 (Model.m:271)
Database.swift文件的第21行是Init()方法中上面代码中指示的那个。
如果Realm崩溃的原因是因为getKey()失败了,那么为什么会失败呢?如果那不是原因,那么失败的其他原因是什么? 如果发生故障,代码可以做什么(例如重试创建Realm对象)?
答案 0 :(得分:0)
我首先要解决这个问题:在这个实例中,这不太可能与你的崩溃有关,但是当你覆盖任何init方法时,你应该总是调用该init方法的超级版本,除非没有超类或没有可用的超类实现来调用。在Objective-C中你将[super init]的结果分配给self,但是,这个模式并没有被swift采用,而且从Apple的文档中不清楚当你从一个直接的NSObject子类中调用super.init()时会发生什么。迅速。我在这个时候失去了动力,以为我花了一些时间在其他地方查看apple / swift GitHub存储库Apple/Swift Github,并且无法找到任何关于从Swift子类调用NSObject init的确实令人满意的信息。如果你真的想知道更多而不仅仅是接受我的话,我肯定会鼓励你继续搜索。在那之前,如果从Swift子类调用NSObject的init()并且它可能在某些时候也可以保存你的屁股,那么它不太可能导致问题! ;)
如果Realm崩溃的原因是因为getKey()失败了,那么为什么会失败呢?
我不确定为什么getKey()可能会失败,但这不太可能是导致崩溃的原因。我相信在调用init()内部的时间代码时,属性的默认值是可用的,在这种情况下,你的应用程序在它到达init()内的调用之前会崩溃。我明天会对此进行一些研究。
如果那不是原因,那么失败的其他原因是什么?
我已经查看了你在那里使用的Realm API,并且不清楚为什么对Realm(配置:)的调用失败了,尽管很明显,因为调用可以抛出,所以预计可以实现。但是,文档没有说明它将抛出的条件。
如果发生故障,代码可以做什么(例如重试创建Realm对象)?
是的!幸运的是,“尝试”机制除了“尝试!”之外还有其他变化。实际上,在这种情况下,应用程序似乎崩溃的原因是你正在尝试使用!在生产代码中,只应在您知道调用极不可能失败的情况下使用,例如从应用程序包内部检索应用程序附带的资源。根据Apple的文档:
有时你知道抛出函数或方法实际上不会在运行时抛出错误。在那些场合,你可以写试试!在表达式之前禁用错误传播并在运行时断言中包装调用,不会抛出任何错误。如果实际抛出了错误,您将收到运行时错误。 Swift Error Handling Documentation
当涉及到影响用户体验时,我总是喜欢谨慎行事,所以即使尝试过一次,我也会感到非常惊讶!在我的任何代码中(甚至Swift游乐场玩具和实验)。
为了优雅地失败并重试或采取其他行动,您的代码需要使用try吗?它会将抛出的错误转换为可选项,如:
if let realm = try? Realm(configuration: configuration) {
// Do something with realm and ignore the error
}
或者您可以使用完整的try-catch机制,如下所示:
let realm: Realm? = nil
do {
realm = try Realm(configuration: configuration)
// do something with realm
} catch let e {
realm = nil
// do something with the error
Swift.print(e)
}
或者:
do {
let realm = try Realm(configuration: configuration)
// do something with realm
} catch let e {
// do something with the error
Swift.print(e)
}
所以这可能不是你从未让这个Realm调用失败的黄金票,但我希望它能为你的代码提供更强大的帮助。我为任何错误道歉,这对我来说已经很晚了。祝你好运和欢呼! :)