You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

UICalendarView装饰缓存触发NSInvalidArgumentException错误排查

UICalendarView缓存日期装饰时触发NSInvalidArgumentException错误

问题复现

代码示例

class Coordinator: NSObject, UICalendarViewDelegate, UICalendarSelectionSingleDateDelegate {
    // ...

    private var decorationCache: [DateComponents: UICalendarView.Decoration?] = [:]

    func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
        if let decoration = decorationCache[dateComponents] {
            return decoration
        }
        let decoration = calculateDecoration(dateComponents: dateComponents)
        decorationCache[dateComponents] = decoration
        return decoration
    }
    // ...
}

错误信息

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSArrayM insertObject:atIndex:]: object cannot be nil'

即使将calculateDecoration(dateComponents:)替换为直接返回UICalendarView.Decoration.default(),崩溃依然会触发。

调用栈关键片段

* thread #1, queue = 'com.apple.uikit.datasource.diffing', stop reason = breakpoint 1.1
  * frame #0: 0x0000000100abf6e4 JobShift`CalendarView.Coordinator.calendarView(calendarView=0x00000001212052d0, dateComponents=Foundation.DateComponents @ 0x000000016f3d7850, self=0x0000000121204fe0) at CalendarView.swift:47:24
    frame #1: 0x0000000100abf880 JobShift`@objc CalendarView.Coordinator.calendarView(_:decorationFor:) at <compiler-generated>:0
    frame #2: 0x00000001aa753d60 UIKitCore`-[UICalendarView _delegateDecorationForDay:] + 108
    frame #3: 0x00000001aa753bdc UIKitCore`-[UICalendarView _configuedCellForCollectionView:indexPath:day:] + 172

错误原因

问题出在缓存字典的类型定义上:

private var decorationCache: [DateComponents: UICalendarView.Decoration?] = [:]

这里字典的值类型是双重可选类型(UICalendarView.Decoration??)。当首次查询某个DateComponents时,字典中没有对应键,返回的nil会被自动包装成Optional(nil)(即UICalendarView.Decoration??.some(nil))。

当这个值被返回给calendarView(_:decorationFor:)方法后,UIKit内部尝试将该值插入到NSMutableArray中,但Foundation的数组不允许存储nil对象,因此触发了-[__NSArrayM insertObject:atIndex:]: object cannot be nil的崩溃。

修复方案

方案1:保留nil缓存,调整查询逻辑

通过检查字典键是否存在,区分“未计算”和“已计算(含nil)”的状态:

class Coordinator: NSObject, UICalendarViewDelegate, UICalendarSelectionSingleDateDelegate {
    // ...

    private var decorationCache: [DateComponents: UICalendarView.Decoration?] = [:]

    func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
        // 先判断键是否存在,避免将"未计算"的nil包装成双重可选
        if decorationCache.keys.contains(dateComponents) {
            return decorationCache[dateComponents] ?? nil
        }
        let decoration = calculateDecoration(dateComponents: dateComponents)
        decorationCache[dateComponents] = decoration
        return decoration
    }
    // ...
}

方案2:简化缓存逻辑,不存储nil结果

如果允许重复计算返回nil的装饰,可以将字典值类型改为非可选:

class Coordinator: NSObject, UICalendarViewDelegate, UICalendarSelectionSingleDateDelegate {
    // ...

    private var decorationCache: [DateComponents: UICalendarView.Decoration] = [:]

    func calendarView(_ calendarView: UICalendarView, decorationFor dateComponents: DateComponents) -> UICalendarView.Decoration? {
        if let decoration = decorationCache[dateComponents] {
            return decoration
        }
        guard let decoration = calculateDecoration(dateComponents: dateComponents) else {
            return nil
        }
        decorationCache[dateComponents] = decoration
        return decoration
    }
    // ...
}

内容的提问来源于stack exchange,提问作者shinking

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 11:49:51