iOS崩溃堆栈跟踪没有多大意义

时间:2017-04-24 12:25:32

标签: ios swift3 crash-reports

我已经获得并设法为我的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) -> ()在某些时候会调用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)")
        }
    }
}

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

更新 虽然我认为堆栈跟踪表明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!
}

1 个答案:

答案 0 :(得分:1)

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

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

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

    如果你无法重现崩溃,我的建议就是重写这段代码而不用强制解包并思考如果出现问题该怎么做。一种解决方案可以是使用可用的初始化器并在上游处理可能的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
        }
    }