如何使用 Swift 处理空对象?

Posted

技术标签:

【中文标题】如何使用 Swift 处理空对象?【英文标题】:How to handle empty objects with Swift? 【发布时间】:2016-09-24 06:12:23 【问题描述】:

我有一个跟踪每月支出的应用程序。我有一个 Expense 实体,该实体具有一个 month 属性,用于跟踪创建当前月份的费用。然后,我将在表格视图中显示每个月的费用,如下所示。用户只有在下个月或上个月有费用时才能左右切换

@IBAction func backMonthButtonPressed(sender: AnyObject) 
    print("Back Button Pressed")
    currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
    if checkMonth(currentMonth) 
        updateFetch()
        setMonth()
     else 
        currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
    
    tableView.reloadData()



@IBAction func nextMonthButtonPressed(sender: AnyObject) 

    print("Next Button Pressed")

    currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
    if checkMonth(currentMonth) 
        updateFetch()
        setMonth()
     else 
        currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
    
    tableView.reloadData()


func checkMonth(month : NSDate) -> Bool 
    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad

    let fetchRequest = NSFetchRequest(entityName: "Expense")
    fetchRequest.predicate = NSPredicate(format: "month == %@", month.MonthYearDateFormatter())
    let count = context.countForFetchRequest(fetchRequest, error: nil)
    if count > 0 
        print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")
        return true

     else 
        print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
        return false
    

我的问题是,在不太可能的情况下,用户在 6 月份创建了一项费用,但在 8 月份没有创建费用。我怎样才能让用户在不跳过它的情况下仍然看到他/她在 8 月份的费用。有任何想法吗?

【问题讨论】:

一次获取所有费用并将它们存储在一个数组中,按月排序。然后只需浏览数组 【参考方案1】:

在阐述之前我做了一些优化:

@IBAction func backMonthButtonPressed(sender: AnyObject) 
    self.processMonth(step: -1)


@IBAction func nextMonthButtonPressed(sender: AnyObject) 
    self.processMonth(step: 1)


// a method uses almost the same code for both cases, so it was merged
func processMonth(step: Int) 
    let direction = (step < 1 ? "Back" : "Next")
    print("\(direction) Button Pressed")

    currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: step, toDate: currentMonth, options: [])!

    //if checkMonth(currentMonth) 
    // I wouldn't test this because it locks you out from seeing empty month.
        updateFetch()
        setMonth()
    //

    tableView.reloadData()

回答您的确切问题:

如果你的 UITableView 的数据源设置正确,你应该可以度过空旷的几个月

// changed return type from `Bool` to `void` as I suggested in the method not to test empty month, as it could be useless

func checkMonth(month : NSDate) 
    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad

    let fetchRequest = NSFetchRequest(entityName: "Expense")
    fetchRequest.predicate = NSPredicate(format: "month == %@", month.MonthYearDateFormatter())
    let count = context.countForFetchRequest(fetchRequest, error: nil)
    if count > 0 
        print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")

     else 
        print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
        // here you can make some additional actions, like seeting the empty table with "no expenses this month"
    

无论如何,正如 @Paulw11 所指出的,如果您的数据源不是大小耗尽的,您可以从数据模型中获取数据,例如 viewDidLoad/viewDidAppear,然后每月渲染根据currentMonth 变量(关于用户当前看到的月份)。

因此,您只能在控制器的负载中以及每次用户更改当前月份视图时调用 setMonth() 方法。

【讨论】:

【参考方案2】:

在@Paulw11 和@pedrouan 的帮助下,我能够做我想做的事。

获取所有费用

    func fetchExpenses() 
    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad
    let fetchRequest = NSFetchRequest(entityName: "Expense")

    //Always Sort Budget by the date it's created
    let sortDescriptor = NSSortDescriptor(key: "created", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]

    do 
        let results = try context.executeFetchRequest(fetchRequest)
        self.expenses = results as! [Expense]
     catch let err as NSError 
        print(err.debugDescription)
    

将约束设置为第一个月和最后一个月,以确定用户可以在 viewDidLoad 中浏览月份的距离

    //If array is not empty, set first and last month
    if expenses.count > 0 
        guard let first = expenses.first?.month else 
            return
        
        firstMonth = first

        guard let last = expenses.last?.month else 
            return
        
        lastMonth = last

    else 
        //Set current month to be first and last
        firstMonth = currentMonth.MonthYearDateFormatter()
        lastMonth = currentMonth.MonthYearDateFormatter()
    

限制用户超过第一个月和上个月

    @IBAction func backMonthButtonPressed(sender: AnyObject) 
    print("Back Button Pressed")
    if currentMonth.MonthYearDateFormatter() != firstMonth 
        self.processMonth(step: -1)
    


@IBAction func nextMonthButtonPressed(sender: AnyObject) 

    print("Next Button Pressed")
    if currentMonth.MonthYearDateFormatter() !=  lastMonth 
        self.processMonth(step: 1)
    

返回当月的所有费用,如果没有明确的取回空表。

  func updateFetch() 
    setFetchResults()

    do 
        try self.fetchedResultsController.performFetch()
     catch 
        let error = error as NSError
        print("\(error), \(error.userInfo)")
    



func setFetchResults() 

    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad

    //Fetch Request
    let fetchRequest = NSFetchRequest(entityName: "Expense")

    let sortDescriptor = NSSortDescriptor(key: "created", ascending: false)

    fetchRequest.predicate = NSPredicate(format: "month == %@", currentMonth.MonthYearDateFormatter())
    fetchRequest.sortDescriptors = [sortDescriptor]
    let count = context.countForFetchRequest(fetchRequest, error: nil)


    let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: ad.managedObjectContext , sectionNameKeyPath: section, cacheName: nil)

    //If there is none, empty fetch request
    if count == 0 
        print("There are no expenses for this month. Show nothing")
        controller.fetchRequest.predicate = NSPredicate(value: false)
     else 
        print("There are expenses for this month. Show expenses")
    

    fetchedResultsController = controller
    controller.delegate = self

使用 pedroran 的 processMonth(),我能够在空的 tableViews 中导航,以便用户可以看到八月是空的,同时仍然能够进入六月的月份。谢谢大家。

【讨论】:

以上是关于如何使用 Swift 处理空对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 API 调用中设置完成处理程序 - Swift 5

java中如何使用空参构造方法自动生成不同名字的对象,使用非静态的属性和静态属性有什么区别,原因是什么?如何理解static关键字

Swift学习笔记之---使用if和let处理空变量

Swift学习笔记之---使用if和let处理空变量

Swift学习笔记之---使用if和let处理空变量

Swift学习笔记之---使用if和let处理空变量