簡體   English   中英

在 swift 和 realm 中展開可選值

[英]Unwrapping an Optional value in swift and realm

我為應用程序寫了一個有效的 function,但出現錯誤“當隱式部署可選值時意外發現 nil 值”限制限制 label.文本我無法修復。

特性:

@IBOutlet weak var limitLabel: UILabel!

Function:

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].limitSum行中limitLabelnil 無論 Realm 即使調用limitLabel.text = "Hello world!"都會崩潰

您始終可以保護您需要避免更改代碼的數據。 只需添加

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)
    }()
}

請注意一些有助於您更好地構建和解決代碼的做法。

  • 首先保護危險數據
  • 為您的方法創建一個可重用項目的列表(應該盡可能少,在大多數情況下沒有)。 請注意以后如何分配這些。 如果你在分配給它之前嘗試使用它,你的編譯器會警告你。
  • 將盡可能多的代碼包裝到封閉的部分中,例如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 { $0.date >= startDate && $0.date < endDate }
            return spendings.reduce(0, { $0 + $1.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 被聲明為“隱式解包 Optional”(注意!類型后的UILabel

@IBOutlet weak var limitLabel: UILabel!

一個隱式展開的 Optional 是一個 Optional,本質上,編譯器添加了一個隱藏的“.”。 每次嘗試引用該對象時強制展開。

這意味着

limitLabel.text = //something

編譯為

limitLabel!.text = //something

如果limitLabel為nil,你就會崩潰。

如果您在加載視圖之前調用leftLabels() function,或者該插座從未連接,您將崩潰。

您可以通過在語句中添加可選的 unwrap 來解決此問題:

limitLabel?.text = //something

(該結構稱為“可選鏈接”。)

鑒於您收到的崩潰消息提到“隱式展開可選值”,這很可能是您的情況崩潰的原因。 但是,您也應該解決其他問題。

第二種崩潰的方法是在數組索引中。

    limitLabel.text = limit[0].limitSum  

當您按索引從數組中獲取 object 時,如果該數組不包含該索引處的項目,您的應用程序將崩潰。 如果limit數組為空,表達式limit[0]將崩潰。

數組類型first有一個計算屬性,如果數組為空,它將返回一個可選項。 您應該將其更改為limit.first?.limitSum

將整行更改為:

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

而且它不會再崩潰了。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM