在 swift 和领域中展开可选值

Unwrapping an Optional value in swift and realm

我为应用程序编写了一个工作函数,但出现错误“当隐式部署可选值时意外发现 nil 值”限制限制 label.the 文本我无法修复。

属性:

@IBOutlet weak var limitLabel: UILabel!

函数:

func leftLabels(){ 
        let limit = self.realm.objects(Limit.self)
        guard limit.isEmpty == false else {return} 
        
        limitLabel.text = limit[0].limitSum //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 
        
        let calendar = Calendar.current 
        
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd HH:mm"
        
        let firstDay = limit[0].limitDate as Date
        let lastDay = limit[0].limitLastDate as Date
        
        let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay) 
        let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay) 
        
        let startDate = formatter.date(from: "\(firstComponent.year!)/\(firstComponent.month!)/\(firstComponent.day!) 00:00") 

        let endDate = formatter.date(from: "\(lastComponent.year!)/\(lastComponent.month!)/\(lastComponent.day!) 23:59")
        
        let filterLimit: Int = realm.objects(SpendingDB.self).filter("self.date >= %@ && self.date <= %@", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
        
        ForThePeriod.text = "\(filterLimit)" 
        
        let a = Int(limitLabel.text!)!
        let b = Int(ForThePeriod.text!)!
        let c = a - b 
        
        availableForSpending.text = "\(c)" 

如果你告诉我正确的密码我会很高兴

根据评论,如果您的视图似乎尚未加载,并且您的某些视图仍在 nil。您的应用程序崩溃是因为在行 limitLabel.text = limit[0].limitSumlimitLabelnil。即使调用 limitLabel.text = "Hello world!"

,它也会崩溃,无论 Realm 是什么

您始终可以保护您需要的数据,以避免更改您的代码。只需添加

guard let limitLabel = limitLabel else { return nil } 
guard let ForThePeriod = ForThePeriod else { return nil }

等等。

我试着清理一下你的代码。很难理解你到底想达到什么目的,但像下面这样的东西似乎更合适一点:

func leftLabels() {
    // Elements needed for method to execute.
    guard let limitLabel = limitLabel else { return }
    guard let forThePeriodLabel = forThePeriodLabel else { return }
    guard let availableForSpendingLabel = availableForSpendingLabel else { return }
    
    // Items that will be reused throughout the method later on
    let limits: [Limit]
    let firstLimit: Limit
    let dates: (start: Date?, end: Date?)
    let filterLimit: Int
    
    limits = self.realm.objects(Limit.self)
    guard limits.isEmpty == false else { return }
    firstLimit = limits[0]
    
    // limitLabel
    limitLabel.text = firstLimit.limitSum
    
    // Date components
    dates = {
        let calendar = Calendar.current
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy/MM/dd HH:mm"
        
        let firstDay = firstLimit.limitDate as Date
        let lastDay = firstLimit.limitLastDate as Date
        
        let firstComponent = calendar.dateComponents([.year, .month, .day], from: firstDay)
        let lastComponent = calendar.dateComponents([.year, .month, .day], from: lastDay)
        
        let startDate = formatter.date(from: "\(firstComponent.year!)/\(firstComponent.month!)/\(firstComponent.day!) 00:00")
        let endDate = formatter.date(from: "\(lastComponent.year!)/\(lastComponent.month!)/\(lastComponent.day!) 23:59")
        
        return (startDate, endDate)
    }()
    
    // forThePeriodLabel
    filterLimit = realm.objects(SpendingDB.self).filter("self.date >= %@ && self.date <= %@", startDate ?? "", endDate ?? "").sum(ofProperty: "cost")
    forThePeriodLabel.text = String(filterLimit)
    
    // availableForSpendingLabel
    availableForSpendingLabel.text = {
        guard let a = Int(firstLimit.limitSum) else { return "" }
        let b = filterLimit
        let c = a - b
        return String(c)
    }()
}

记下一些有助于您更好地构建和解决代码的做法。

  • 危险数据先防
  • 为您的方法创建一个可重复使用的项目列表(在大多数情况下应该越少越好 none)。请注意稍后如何将这些分配给。如果你在分配给它之前尝试使用它,你的编译器会警告你。
  • 将尽可能多的代码包装到封闭的部分中,例如 availableForSpendingLabel.text = { ... code here ... }()
  • 使用元组,例如let dates: (start: Date?, end: Date?)
  • 不要害怕使用长名称,例如 availableForSpendingLabel

我什至会进一步尝试将其分解为多种方法。但是我不确定这个方法是做什么的,假设你只发布了其中的一部分...

========== 编辑:添加替代方法 ==========

根据评论,这是一个财务应用程序,因此可能至少处理 Decimal 个数字是有意义的。还介绍了添加内部解析数据的新结构的方法。格式化程序也用于格式化数字。以及其他一些改进:

struct Limit {
    let amount: Decimal
    let startDate: Date
    let endDate: Date
}

struct Spending {
    let cost: Decimal
    let date: Date
}

struct LimitReport {
    let limitAmount: Decimal
    let spendingSum: Decimal
    let balance: Decimal
    
    init(limit: Limit) {
        let limitAmount: Decimal = limit.amount
        let spendingSum: Decimal = {
            let calendar = Calendar.autoupdatingCurrent // Is this OK or should it be some UTC or something?
            func beginningOfDate(_ date: Date) -> Date {
                let components = calendar.dateComponents([.day, .month, .year], from: date)
                return calendar.date(from: components)!
            }
            let startDate = beginningOfDate(limit.startDate)
            let endDate = calendar.date(byAdding: .day, value: 1, to: startDate)
            
            let spendings: [Spending] = realm.objects(Spending.self).filter { [=12=].date >= startDate && [=12=].date < endDate }
            return spendings.reduce(0, { [=12=] + .cost })
        }()
        let balance = limitAmount - spendingSum
        
        self.limitAmount = limitAmount
        self.spendingSum = spendingSum
        self.balance = balance
    }
    
}

func leftLabels() {
    // Elements needed for method to execute.
    guard let limitLabel = limitLabel else { return }
    guard let forThePeriodLabel = forThePeriodLabel else { return }
    guard let availableForSpendingLabel = availableForSpendingLabel else { return }
    
    guard let limit = self.realm.objects(Limit.self).first else { return }
    
    let formatter = NumberFormatter()
    formatter.numberStyle = .currency
    formatter.currencySymbol = "$"
    
    let report = LimitReport(limit: limit)
    
    limitLabel.text = formatter.string(from: report.limitAmount)
    forThePeriodLabel.text = formatter.string(from: report.spendingSum)
    availableForSpendingLabel.text = formatter.string(from: report.balance)
}

Matic 为您的问题(已投票)提供了一个很好、全面的答案。我想我会提供一个专门针对您的崩溃的答案以及一个“简短而甜蜜”的修复方法:

有问题的行可能会以两种不同的方式崩溃:

limitLabel.text = limit[0].limitSum //Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value 

您的 limitLabel IBOutlet 被声明为“隐式解包可选”(注意类型后的 !UILabel:

@IBOutlet weak var limitLabel: UILabel!

隐式展开的 Optional 是一个 Optional,本质上,编译器添加了一个隐藏的“!”每次尝试引用该对象时强制解包。

也就是说

limitLabel.text = //something

编译为

limitLabel!.text = //something

如果 limitLabel 为零,你就会崩溃。

如果您在加载视图之前调用 leftLabels() 函数,或者如果该插座从未连接,您将会崩溃。

您可以通过向语句添加可选的解包来解决此问题:

limitLabel?.text = //something

(该构造称为“可选链接”。)

鉴于您收到的崩溃消息提到“隐式解包一个可选值”,很可能这就是您的案例中发生崩溃的原因。但是,您还应该解决其他问题。

导致崩溃的第二种方式是数组索引。

    limitLabel.text = limit[0].limitSum  

当您按索引从数组中获取对象时,如果数组不包含该索引处的项目,您的应用程序将会崩溃。如果 limit 数组为空,表达式 limit[0] 将崩溃。

数组类型有一个计算的 属性 first,如果数组为空,它将 return 一个可选的。 您应该将其更改为 limit.first?.limitSum.

将整行更改为:

    limitLabel?.text = limit.first()?.limitSum

它不会再崩溃了。