2017-07-21 40 views
7

Mam wydaną aplikację przy użyciu dziedziny i istnieje kilka dzienników awarii pokazujących, że czasami nie można utworzyć obszaru z konfiguracją powodującą awarię EXC_BREAKPOINT (SIGTRAP). (Jest 9 plików zderzeniowe za kilkaset instalacji aplikacji więc nie czymś, co często dzieje)Co może być przyczyną sporadycznego niepowodzenia utworzenia zaszyfrowanego obiektu 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 
    } 

Oto odnośny fragment pliku katastrofy:

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) 

Linia 21 Database.swift plik jest wskazany w powyższym kodzie w metodzie Init().

Jeśli przyczyną awarii w Królestwie jest to, że metoda getKey() nie działa, dlaczego to się nie udało? Jeśli to nie jest przyczyną, to jakie są inne powody niepowodzenia? Czy jest coś, co może zrobić kod (na przykład spróbuj utworzyć obiekt Realm), jeśli wystąpi awaria?

Odpowiedz

0

Sposób startowych

Zamierzam dostać to z pierwszej okazji: Jest mało prawdopodobne, że to nie ma nic wspólnego z katastrofą w tym przypadku, ale kiedy zastąpić dowolną metodę init ty powinien zawsze wywoływać wersję super tej metody init, chyba że nie ma super klasy ani żadnej dostępnej implementacji nadklasy. W Objective-C przypisuje się wyniki samego [super init], jednak ten wzorzec nie został przyjęty przez swift i nie jest jasne, z dokumentacji Apple, co się dzieje, gdy wywołuje się super.init() z bezpośredniej podklasy NSObject w Szybki. W tej godzinie brakuje mi czasu, pomyślałem, że spędziłem trochę czasu patrząc na jabłko/szybkie repozytorium GitHub Apple/Swift Github między innymi i nie byłem w stanie znaleźć żadnych naprawdę satysfakcjonujących informacji o wywołaniu inicjacji NSObject z podklasy Swift. Zdecydowanie zachęcam do kontynuowania wyszukiwania, jeśli naprawdę chcesz wiedzieć więcej, a nie tylko wierzyć mi na słowo. Do tego czasu jest bardzo mało prawdopodobne, że spowoduje to problemy, jeśli wywołasz init() NSObject z podklasy Swift i to może w pewnym momencie uratować twój tyłek! ;)

katastrofie

Jeśli powodem katastrofy Realm dlatego getKey() nie powiedzie się, to dlaczego miałoby to być w przypadku braku?

Nie jestem pewien, dlaczego funkcja getKey() może być awarią, ale jest mało prawdopodobne, że jest to przyczyną awarii. Wierzę, że domyślne wartości właściwości są dostępne przez kod czasu w wywołaniu init(), w którym to przypadku aplikacja ulegnie awarii, zanim osiągnie to wywołanie wewnątrz init(). Zrobię trochę więcej badań na ten temat jutro.

Jeśli to nie jest przyczyna, to jakie są inne powody niepowodzenia?

Spojrzałem na interfejs Realm API, którego tam używasz, i nie jest jasne, dlaczego wywołanie Realm (konfiguracja :) zawodzi, chociaż jest jasne, że prawdopodobnie będzie to możliwe, ponieważ połączenie może zostać wykonane. Jednak dokumentacja nie określa warunków, w których będzie rzutować.

Czy jest coś, co może zrobić kod (np. Ponowienie w celu utworzenia obiektu Realm) w przypadku awarii?

Tak! Na szczęście mechanizm "try" ma inną odmianę niż "spróbuj!". Właściwie w tym przypadku przyczyną awarii aplikacji jest próba użycia! który w kodzie produkcyjnym powinien być używany tylko w sytuacjach, gdy wiesz, że wywołanie jest wyjątkowo mało prawdopodobne, aby kiedykolwiek zawiodło, na przykład pobieranie zasobu, z którego twoja aplikacja pochodzi z wewnątrz pakietu aplikacji. Na dokumentację Apple:

Czasami wiesz, funkcja lub metoda rzucania nie spowoduje błędu w czasie wykonywania. Przy tych okazjach możesz napisać wypróbuj! przed wyrażeniem, aby wyłączyć propagację błędu i zawijać wywołanie w asercji środowiska wykonawczego, aby żaden błąd nie został zgłoszony. Jeśli błąd zostanie zgłoszony, pojawi się błąd runtime. Swift Error Handling Documentation

Jeśli chodzi o wpływ na doświadczenie użytkownika, zawsze lubię zaważyć na ostrożności więc byłbym bardzo zaskoczony, aby znaleźć nawet jednego wystąpienia spróbować! w każdym z moich kodów (nawet zabawek i eksperymentów Swift na placu zabaw).

Aby nie zadziałać z wdziękiem, ponowić próbę lub podjąć inną czynność, należy użyć kodu? który przekonwertuje rzucony błąd na opcjonalny jak w:

if let realm = try? Realm(configuration: configuration) { 
    // Do something with realm and ignore the error 
} 

Albo można użyć pełnego mechanizmu try-catch tak:

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

lub alternatywnie:

do { 
    let realm = try! Realm(configuration: configuration) 
    // do something with realm 
} catch let e { 
    // do something with the error 
    Swift.print(e) 
} 


To może nie być twoje złoty bilet, by nigdy nie mieć połączenia z Realem, ale mam nadzieję, że pomoże to w poprawieniu twojego kodu. Przepraszam za wszelkie błędy, robi się dla mnie późno. Powodzenia i okrzyki! :)

+0

Dziękuję za poświęcenie czasu na odpowiedź. – Gruntcakes