【问题标题】:iOS crash stack trace makes little senseiOS 崩溃堆栈跟踪毫无意义
【发布时间】:2017-04-25 04:08:30
【问题描述】:

我已经获得并成功地为我的 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 级的。 在我的理解中,这意味着 Database.checkSchemaVersion(connection : Connection) -&gt; () 在某些时候会调用 PersonRepository.hydrate(Row) -&gt; 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)")
        }
    }
}

任何想法堆栈跟踪(不是很堆叠,是吗?)应该是什么意思,或者它是如何到达这种情况的?如果事情真的以这种方式发展,难怪它会崩溃。

更新 虽然我相信堆栈跟踪表明在 Database.checkSchemaVersion(connection : Connection) -&gt; () 的某个地方有一个对 PersonRepository.hydrate(Row) -&gt; 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!
}

【问题讨论】:

  • 看起来此崩溃日志已被删除。您能否也发布 PersonRepository 的代码。看起来它来到 `PersonRepository.getInstance().updateSchema(from: schemaVersion, to: build)` 行
  • @dRAGONAIR - 我添加了该类的一部分,但我认为它不相关。从堆栈跟踪中可以看出,有一个从Database.checkSchemaVersion(connection : Connection)PersonRepository.hydrate(Row) 的直接调用——但没有任何这样的调用。如果它不是直接的,那么这两者之间应该有中间层。调用堆栈就是这样工作的,不是吗?

标签: ios swift3 crash-reports


【解决方案1】:

一个提示 - 尽可能避免!。特别是对于try 的异常处理。事情可以抛出应该有一个很好的理由,我相信这就是你崩溃的原因。

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

  1. checkSchemaVersion(connection : Connection)
  2. PersonRepository.getInstance()
  3. PersonRepository.init()
  4. try! db.run()
  5. 抛出异常,因为在Database 中发生了一些可疑的事情

如果您无法重现崩溃,我的建议是在不强制解包的情况下重写此代码,并考虑万一出现问题该怎么办。一种解决方案可能是使用可失败的初始化程序并处理上游可能的 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
    }
}

【讨论】:

  • 谢谢,关于try! 的观点。我已经通过其他方式发现了这个错误 - 结果我已经在TaskRepository 上的init()updateSchema() 中定义了一个新列(所以它甚至不是这个存储库)并且它适用于从以前版本升级的应用程序,但是当新安装应用程序时,它会因为try! 而崩溃。至于解释堆栈跟踪中从第 4 级跳转到第 3 级的主要任务,它仍然没有意义,因为hydrate() 方法仅用于从数据库加载的数据,并且在我的应用程序中没有初始化代码这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-27
  • 2011-06-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多