2017-04-24 132 views
2

我已经获取并设法为我的iOS应用程序提供崩溃报告。但是,看着堆栈轨迹,我似乎无法理解它。iOS崩溃堆栈跟踪没什么意义

Thread 0 Crashed: 
0 libswiftCore.dylib     0x00000001005d330c specialized _assertionFailure(StaticString, String, file : StaticString, line : UInt, flags : UInt32) -> Never (__hidden#17347_:134) 
1 libswiftCore.dylib     0x00000001004d7d2c Error<A where ...>._code.getter (__hidden#18979_:221) 
2 libswiftCore.dylib     0x00000001004d7bf4 swift_errorInMain + 0 
3 MyAppName        0x00000001000c2190 specialized PersonRepository.hydrate(Row) -> Person (PersonRepository.swift:0) 
4 MyAppName        0x00000001000fbebc specialized Database.checkSchemaVersion(connection : Connection) ->() (Database.swift:0) 
5 MyAppName        0x00000001000fc254 _TTSf4d_g__TZFC6MyAppName8DatabaseP33_909B711B8156620EE1EFE30EC21C4C0C11getInstancefT_S0_ (Database.swift:0) 
6 MyAppName        0x00000001000fade8 static Database.getInstance() -> Database (Database.swift:0) 
7 MyAppName        0x00000001000a7a6c TaskRepository.init() -> TaskRepository (TaskRepository.swift:38) 
(......) 

是什么让我抓我的头是如何在地球上也从第4级获得高达3级 在我undertanding,它意味着Database.checkSchemaVersion(connection : Connection) ->()在某些时候调用PersonRepository.hydrate(Row) -> Person - 这使得没有任何意义。

这里是我完整的数据库类(它不是太大)

// Database.swift 
import SQLite 
import Foundation 

class Database 
{ 
    private var connection : Connection? 
    private static var instance : Database? 

    private static func getInstance() -> Database 
    { 
     if (instance == nil) { 
      instance = Database() 
      instance!.checkSchemaVersion(connection: instance!.connection!) 
     } 
     return instance! 
    } 

    private init() 
    { 
     let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first! 
     connection = try! Connection("(path)/myappname.sqlite3"); 
    } 

    deinit { 
     connection = nil 
    } 

    static func getConnection() -> Connection 
    { 
     return getInstance().connection! 
    } 

    func checkSchemaVersion(connection : Connection) 
    { 
     guard let buildString = Bundle.main.infoDictionary?["CFBundleVersion"] as? String else { 
      Application.debug("Database: could not convert Build to String") 
      return 
     } 
     guard let build = Int64(buildString) else { 
      Application.debug("Database: could not convert Build from String to Int64") 
      return 
     } 
     let schemaVersion = try! connection.scalar("PRAGMA user_version") as? Int64 ?? 0 
     Application.debug("Database: checking schema version - DB=(schemaVersion) vs app=(build)") 
     if schemaVersion < build { 
      Application.debug("Database: old schema version detected - updating now") 
      PersonRepository.getInstance().updateSchema(from: schemaVersion, to: build) 
      PictureRepository.getInstance().updateSchema(from: schemaVersion, to: build) 
      TaskRepository.getInstance().updateSchema(from: schemaVersion, to: build) 
      TaskCommentRepository.getInstance().updateSchema(from: schemaVersion, to: build) 
      VenueRepository.getInstance().updateSchema(from: schemaVersion, to: build) 
      try! connection.run("PRAGMA user_version = (build)") 
     } 
    } 
} 

任何想法是什么堆栈跟踪(是不是很stacky,是吧?)应该是说,或者它是如何在这种情况下抵达?难怪事情实际上是以这种方式进行的。

UPDATE 虽然我相信堆栈跟踪说某处Database.checkSchemaVersion(connection : Connection) ->()有到PersonRepository.hydrate(Row) -> Person直接调用,这除了是多余的,这里的PersonRepository

private init() 
{ 
    db = Database.getConnection() 
    table = Table("persons") 

    pictureRepository = PictureRepository.getInstance() 

    try! db.run(table.create(ifNotExists: true) { 
     t in 
     t.column(id, primaryKey: true) 
     t.column(name) 
     t.column(email) 
     t.column(phone) 
     t.column(company) 
     t.column(pictureId) 
    }) 
} 

public func updateSchema(from: Int64, to: Int64) 
{ 
    if from < 2016121201 { 
     try! db.run(table.addColumn(active, defaultValue: 1)) 
    } 
} 

static func getInstance() -> PersonRepository 
{ 
    if (instance == nil) { 
     instance = PersonRepository() 
    } 
    return instance! 
} 
+0

看起来像这样崩溃日志被剥离。你也可以发布PersonRepository的代码。 看起来像来到'PersonRepository.getInstance()。updateSchema(from:schemaVersion,to:build)'line – dRAGONAIR

+0

@dRAGONAIR - 我已经添加了该类的一部分,但是我没有发现它相关。从堆栈跟踪中,应该看到有一个从'Database.checkSchemaVersion(connection:Connection)'直接调用到'PersonRepository.hydrate(Row)' - 但是没有这样的。如果不是直接的,那么这两者之间应该存在中介水平。这就是调用堆栈的工作方式,不是吗? – mkilmanas

回答

1

一个提示的相关部分 - 避免!越多越好。特别是对于使用try的异常处理。应该有一个很好的理由为什么事情可以抛出,我相信这是你崩溃的原因。

堆栈跟踪可能并不完全明显,因为某些方法调用可以由编译器对发布版本进行内联/优化。在你的情况下,它可能会发生这样:

  1. checkSchemaVersion(connection : Connection)
  2. PersonRepository.getInstance()
  3. PersonRepository.init()
  4. try! db.run()
  5. 抛出异常,因为事情是腥发生在Database

如果你不能重现我的建议会导致的崩溃不用强制重写这些代码 - 解开并思考如果出现问题应该怎么做。一种解决方案可以是使用failable initialisers和上游处理可能nil值,即

private init?() 
{ 
    db = Database.getConnection() 
    table = Table("persons") 

    pictureRepository = PictureRepository.getInstance() 

    do { 
     try db.run(table.create(ifNotExists: true) { 
      t in 
      t.column(id, primaryKey: true) 
      t.column(name) 
      t.column(email) 
      t.column(phone) 
      t.column(company) 
      t.column(pictureId) 
     }) 
    } catch { 
     return nil 
    } 
} 
+0

谢谢,关于'try!'的提示。我已经通过其他方法发现了这个bug - 事实证明,我已经在TaskRepository的'init()'和'updateSchema()'中定义了一个新列(所以它甚至不是这个存储库),它对于应用程序从以前的版本升级,但当应用程序刚刚安装时,会因'try!'而崩溃。 至于解释在堆栈跟踪中从级别4跳转到级别3的主要任务,它仍然没有意义,因为'hydrate()'方法仅用于从DB加载的数据,并且没有初始化代码在我的应用程序 – mkilmanas

相关问题