2017-07-21 49 views
7

我已經發布了一個使用Realm的應用程序,並且有一些崩潰日誌顯示有時會導致EXC_BREAKPOINT(SIGTRAP)崩潰導致創建領域失敗。 (有9個幾百應用安裝防撞文件,所以它不是東西,這是經常發生的事情)偶爾無法創建加密的Realm對象的原因是什麼?

@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

init方法

我要得到這一點的第一個方式:這是不可能的,這有什麼關係在這種情況下你的崩潰,但是當你重寫任何init方法,你應該總是調用超級版本的init方法,除非沒有超類或沒有可用的超類實現來調用。在Objective-C中,你將[super init]的結果賦給self,但是,這種模式並沒有被swift採用,並且從Apple的文檔中不清楚,當你從一個直接NSObject子類調用super.init()迅速。我在這個小時裏沒勁,以爲我確實花了一些時間在其他地方查看Apple/Swift GitHub存儲庫Apple/Swift Github,並且無法從Swift子類中找到任何有關調用NSObject init的真正令人滿意的信息。我絕對會鼓勵你繼續搜索,如果你真的想知道更多,而不是隻聽我的話。在那之前,如果你從Swift子類中調用NSObject的init(),那麼它很可能不會引發問題,並且它也可能在某個時刻保存你的屁股! ;)

崩潰

如果王國崩潰的原因是因爲信息getKey()失敗,那麼爲什麼會是失敗的?

我不確定爲什麼getKey()可能會失敗,但這不太可能是您的崩潰的原因。我相信init()中的時間代碼會調用屬性的默認值,在這種情況下,您的應用程序會在init()內部到達該調用之前崩潰。我明天會對此做更多的研究。

如果那不是原因,那麼失敗的原因是什麼?

我已經看了一下你在那裏使用的Realm API,目前還不清楚爲什麼調用Realm(配置:)失敗,雖然很明顯這是因爲調用可能會拋出。但是,文件沒有說明它會拋出的條件。

有沒有什麼代碼可以做的(比如重試創建Realm對象)如果出現故障?

是的!幸運的是,「try」機制除了「try!」之外還有其他變體。實際上,在這種情況下,應用程序似乎崩潰的原因是您正在使用try!在產品代碼中只能用於您知道該調用極不可能失敗的情況,例如從您的應用程序包中檢索您的應用程序附帶的資源。每個蘋果的文檔:

有時你知道拋出函數或方法實際上不會在運行時拋出一個錯誤。在那些場合,你可以試試!在表達式之前禁用錯誤傳播並將該調用包裝在運行時斷言中不會拋出錯誤。如果實際發生錯誤,您將收到運行時錯誤。 Swift Error Handling Documentation

當涉及到影響用戶的體驗,我總是喜歡在小心的邊犯錯,所以我將非常驚訝地發現,即使出現一次嘗試的!在我的任何代碼(甚至斯威夫特操場玩具和實驗)。

爲了優雅地失敗並重試或採取另一個行動你的代碼或者需要使用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) 
} 


所以這可能不是你的金門票永遠不會有這個領域通話失敗,但我希望它提供了一些幫助,使您的代碼更強大一點。我對任何錯誤表示歉意,對我來說這裏遲到了。祝你好運,歡呼! :)

+0

謝謝你花時間回答。 – Gruntcakes

相關問題