访问数据库对象突然抛出 EXC_BAD_INSTRUCTION

问题描述

我有一个我自己无法解决的问题。

我正在使用 Vapor4、Fluent 和 postgres 数据库作为后端应用程序。 (见下面的Package.swift

有几种不同的模型 TransactionPlanItemBudget,它们对于收集金融统计数据都很有趣。在其他字段中,它们都有一个有效的日期间隔。此功能Statisticable 协议(见下文)

强制执行

如果我现在根据 DateInterval 提供的值创建一个 Statisticable,每个 TransactionPlanItem 工作正常,只有访问 Budget 对象会抛出以下错误

NIO-ELT-0-#8 (10): EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP,subcode=0x0)

NIO-ELT-0-#8 (10): EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP,subcode=0x0)

这里唯一的区别是 Budget 不直接符合 StatisticableBudget 对象被包裹在符合 BudgetIntervalStatisticable 对象内,并被传递到统计生成器中。

现在的问题是:这里有什么问题?怎样才能找出原因?

Package.swift

// swift-tools-version:5.2
import PackageDescription

let package = Package(
    name: "backend",platforms: [
       .macOS(.v10_15)
    ],dependencies: [
        .package(url: "https://github.com/vapor/vapor.git",from: "4.35.0"),.package(url: "https://github.com/vapor/fluent.git",from: "4.0.0"),.package(url: "https://github.com/vapor/fluent-postgres-driver.git",from: "2.0.0"),.package(url: "https://github.com/vapor/jwt.git",from: "4.0.0-rc.2"),.package(url: "https://github.com/iLem0n/SwiftyBeaver.git",.exact("1.8.4")),.package(url: "../SwiftSpec",.branch("master")),.package(url: "https://github.com/Maxim-Inv/SwiftDate.git",.package(url: "https://github.com/vapor/queues.git",from: "1.0.0"),.package(url: "https://github.com/vapor/queues-redis-driver.git",from: "1.0.0-rc.1"),.package(url: "https://github.com/dehesa/CodableCSV",from: "0.6.2")
    ],targets: [
        .target(
            name: "App",dependencies: [
                .product(name: "Fluent",package: "fluent"),.product(name: "FluentPostgresDriver",package: "fluent-postgres-driver"),.product(name: "Vapor",package: "vapor"),.product(name: "JWT",package: "jwt"),.product(name: "SwiftyBeaver",package: "SwiftyBeaver"),.product(name: "SwiftDate",package: "SwiftDate"),.product(name: "SwiftSpec",package: "SwiftSpec"),.product(name: "CodableCSV",package: "CodableCSV"),.product(name: "Queues",package: "queues"),.product(name: "QueuesRedisDriver",package: "queues-redis-driver")
            ],swiftSettings: [           
                .unsafeFlags(["-cross-module-optimization"],.when(configuration: .release))
            ]
        ),.target(name: "Run",dependencies: [.target(name: "App")]),.testTarget(name: "AppTests",dependencies: [
            .target(name: "App"),.product(name: "XCTVapor",])
    ]
)

Statisticable.swift

public protocol Statisticable: CustomDebugStringConvertible {
    var amountPerInstance: Double { get }
    var repetition: RepetitionType { get }
    var validFrom: Date { get }
    var validUntil: Date? { get }
    var validInterval: DateInterval? { get }
}

StatisticsGenerator.swift

public class StatisticsGenerator {
    private let latestBalance: Balance
    
    init(latestBalance: Balance) {
        self.latestBalance = latestBalance
    }
    
    func gatherStatistics(_ elements: [Statisticable],initial: Balance,lastDate: Date) -> [Date: DayStatistic] {
        var result: [Date: DayStatistic] = [:]
        
        let interval = DateInterval(start: initial.date,end: lastDate)
        for element in elements {

            switch element.repetition {
            case .once:
                if interval.contains(element.validFrom) {
                    upsert(amount: element.amountPerInstance,at: element.validFrom,result: &result)
                }
            default:       

                /* HERE IS THE ERROR,but only if the object is originally a `BudgetInterval` */
                let elementInterval = DateInterval(start: element.validFrom,end: element.validUntil ?? lastDate)
                
                                
                /* ... */
            }
        }
        
        
        
        return result
    }
    
    /* ... */ 
}

BudgetInterval.swift

struct BudgetInterval {
    let budget: Budget
    let interval: DateInterval
}

extension BudgetInterval: Statisticable {
    // accessing `budget.validFrom` throws error
    var validFrom: Date {        
        return max(budget.validFrom,interval.start)
    }
    
    var validUntil: Date? {
        if let validUntil = budget.validUntil {
            return min(validUntil,interval.end)
        }
        return interval.end
    }
    
    var repetition: RepetitionType {
        .daily
    }
    
    var amountPerInstance: Double {
        budget.maxExpense / Double(interval.numberOf(.day)!)
    }
}

Budget.swift

final class Budget: Model {
    static let schema: String = "budgets"
    
    @ID(key: .id)                   var id: UUID?
    @Parent(key: .ownerId)          var owner: User
    
    @Field(key: .name)              var name: String
    @Field(key: .maxExpense)        var maxExpense: Double
    @Field(key: .validFrom)         var validFrom: Date
    @Field(key: .validUntil)        var validUntil: Date?    
    
    /* init stuff */
}

Transaction.swift(用于比较)

final class Transaction: Model {
    static var schema: String = "transactions"

    @ID(custom: .id)                    var id: String?
    @Parent(key: .ownerId)              var owner: User
    @Field(key: .receiver)              var receiver: String?
    @Field(key: .reason)                var reason: String
    @Field(key: .amount)                var amount: Double
    @Field(key: .date)                  var date: Date

    /* init stuff */
 
}

extension Transaction: Statisticable {    
    var amountPerInstance: Double {
        return self.amount
    }
    
    // accessing this works fine
    var validFrom: Date {
        return self.date.dateAtStartOf(.day).date
    }
    
    var validUntil: Date? {        
        return self.date.dateAtEndOf(.day).date
    }
}

解决方法

发现问题不在于数据库访问本身,而只是我生成了一个开始日期在结束日期之后的场景。因此无法构建 DateInterval。 在这种情况下,我对 Foundation 类不检查这种常见问题感到有点失望。

无论如何,如果我之前检查过这个边缘情况,它会起作用。

谢谢

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...