2017-07-21 15 views
7

Tôi đã có một ứng dụng được phát hành bằng cách sử dụng Realm và có một số nhật ký sự cố cho thấy đôi khi không tạo được cõi bằng cấu hình dẫn đến lỗi EXC_BREAKPOINT (SIGTRAP). (Có 9 file crash cho một vài trăm cài đặt ứng dụng để không phải cái gì của nó mà đang xảy ra thường xuyên)Điều gì có thể là nguyên nhân gây ra lỗi không thường xuyên khi tạo đối tượng Realm được mã hóa?

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

Dưới đây là phần có liên quan của tập tin vụ tai nạn:

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) 

dòng 21 của Database.swift tệp là tệp được chỉ ra trong mã ở trên trong phương thức Init().

Nếu lý do cho sự cố Realm là do getKey() không thành công thì tại sao điều đó lại thất bại? Nếu đó không phải là nguyên nhân, thì những lý do khác cho sự thất bại là gì? Và có bất cứ điều gì mã có thể làm (chẳng hạn như thử lại để tạo đối tượng Realm) nếu có sự cố không?

Trả lời

0

Phương thức init

tôi sẽ có được điều này ra khỏi con đường đầu tiên: Nó không chắc rằng điều này có bất cứ điều gì để làm với vụ tai nạn của bạn trong trường hợp này, nhưng khi bạn ghi đè lên bất kỳ phương pháp init, bạn nên luôn gọi phiên bản siêu của phương thức init đó trừ khi không có siêu lớp hoặc không có triển khai siêu lớp có sẵn để gọi. Trong Objective-C, bạn gán kết quả của [super init] cho self, tuy nhiên, pattern này không được chấp nhận bởi swift và không rõ ràng, từ tài liệu của Apple, điều gì sẽ xảy ra khi bạn gọi super.init() từ lớp con NSObject trực tiếp trong Nhanh. Tôi đang hết hơi vào giờ này, nghĩ rằng tôi đã dành thời gian nhìn vào kho lưu trữ GitHub táo/nhanh chóng Apple/Swift Github ở một nơi khác và không thể tìm thấy bất kỳ thông tin thực sự thỏa mãn nào về việc gọi NSObject init từ một lớp con Swift. Tôi chắc chắn sẽ khuyến khích bạn tiếp tục tìm kiếm nếu bạn thực sự muốn biết nhiều hơn và không chỉ dùng từ ngữ của tôi cho nó. Cho đến lúc đó, rất khó có khả năng nó sẽ gây ra vấn đề nếu bạn gọi init của NSObject() từ lớp con Swift và nó cũng có thể tiết kiệm mông của bạn tại một số điểm! ;)

Vụ tai nạn

Nếu lý do cho sự sụp đổ của Vương quốc là vì getKey() thất bại, thì tại sao đó sẽ là thất bại?

Tôi không chắc tại sao getKey() có thể bị lỗi, nhưng không chắc đây là nguyên nhân gây ra sự cố của bạn. Tôi tin rằng giá trị mặc định cho các thuộc tính có sẵn bởi mã thời gian bên trong init() được gọi là trong trường hợp ứng dụng của bạn sẽ sụp đổ trước khi nó đạt đến cuộc gọi đó bên trong init(). Tôi sẽ nghiên cứu thêm một chút vào ngày mai.

Nếu đó không phải là nguyên nhân, lý do nào khác cho sự thất bại?

Tôi đã xem API Realm bạn đang sử dụng ở đó và không rõ tại sao cuộc gọi đến Realm (cấu hình :) bị lỗi mặc dù rõ ràng là có thể thực hiện được kể từ khi cuộc gọi có thể thực hiện. Tuy nhiên, tài liệu không nêu rõ các điều kiện theo đó nó sẽ ném.

Và có bất kỳ điều gì mã có thể làm (chẳng hạn như thử lại để tạo đối tượng Realm) nếu có sự cố không?

Có! May mắn thay, cơ chế "thử" có các biến thể khác hơn là chỉ "thử"! Trên thực tế, trong trường hợp này, lý do ứng dụng dường như bị lỗi là bạn đang sử dụng thử! mà trong mã sản xuất chỉ nên được sử dụng trong các tình huống mà bạn biết cuộc gọi là vô cùng không bao giờ thất bại như lấy một nguồn tài nguyên ứng dụng của bạn tàu với từ bên trong nó là gói ứng dụng. Theo tài liệu của Apple:

Đôi khi bạn biết chức năng hoặc phương pháp ném sẽ không xảy ra lỗi khi chạy. Trong những dịp đó, bạn có thể viết thử! trước biểu thức để vô hiệu hóa tuyên truyền lỗi và bọc cuộc gọi trong xác nhận thời gian chạy mà không có lỗi nào sẽ được ném. Nếu một lỗi thực sự được ném, bạn sẽ gặp phải lỗi thời gian chạy. Swift Error Handling Documentation

Khi nói đến ảnh hưởng đến kinh nghiệm người dùng, tôi luôn luôn muốn sai lầm về phía thận trọng vì vậy tôi sẽ rất ngạc nhiên khi thấy dù chỉ một lần xuất hiện của thử! trong bất kỳ mã nào của tôi (ngay cả đồ chơi và thí nghiệm của sân chơi Swift).

Để không thành công và thử lại hoặc thực hiện một hành động khác mà mã của bạn cần sử dụng thử? mà sẽ chuyển đổi các lỗi ném vào một tùy chọn như trong:

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

Hoặc bạn có thể sử dụng một cơ chế đầy đủ try-catch như vậy:

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

Hoặc cách khác:

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


Vì vậy, điều này có thể không phải của bạn vé vàng để không bao giờ có cuộc gọi Realm này thất bại, nhưng tôi hy vọng nó cung cấp một số trợ giúp để làm cho mã của bạn mạnh mẽ hơn một chút. Tôi xin lỗi vì bất kỳ lỗi nào, nó đến trễ với tôi ở đây. Chúc may mắn và cổ vũ! :)

+0

Cảm ơn bạn đã dành thời gian trả lời. – Gruntcakes

Các vấn đề liên quan