使用 nil 必需属性保存上下文时,Core Data 自定义访问器崩溃

Core Data custom accessor crash when saving context with nil required attribute

阅读 this excellent article 关于 Swift 中自定义访问器的内容后,我重构了 NSDecimalNumber 以使用新的 Decimal 类型。我有一个相当复杂的模型,几天来一切正常,但如果我在必需的(非可选的)属性 为零时保存托管对象上下文,我现在看到 EXC_BAD_ACCESS 崩溃, baseAmount 就我而言。直接使用NSDecimalNumber时,重构前保存对象会失败,但不会崩溃。

不确定这是否重要,但我正在使用 Magical Record 的功能保存上下文:NSManagedObjectContext.mr_default().mr_saveToPersistentStoreAndWait()

关于如何修改 baseAmount 访问器以实现与 Core Data 提供的相同的失败保存行为以使其不会崩溃的任何想法?

这是我的 NSManagedObject 子类的相关部分,后面是堆栈跟踪:

extension DFTransaction {

    @NSManaged private var primitiveBaseAmount: NSDecimalNumber

    var baseAmount: Decimal {
        get {
            willAccessValue(forKey: "baseAmount")
            defer { didAccessValue(forKey: "baseAmount") }
            return primitiveBaseAmount.decimalValue
        }
        set {
            willChangeValue(forKey: "baseAmount")
            defer { didChangeValue(forKey: "baseAmount") }
            primitiveBaseAmount = NSDecimalNumber(decimal: newValue)
        }
    }
}


[appname]`@objc DFTransaction.baseAmount.getter:
0x100343cd0 <+0>:   stp    x29, x30, [sp, #-16]!
0x100343cd4 <+4>:   mov    x29, sp
0x100343cd8 <+8>:   sub    sp, sp, #96               ; =96 
0x100343cdc <+12>:  stur   x0, [x29, #-32]
0x100343ce0 <+16>:  stur   x8, [x29, #-40]
0x100343ce4 <+20>:  bl     0x10051ca64               ; symbol stub for: objc_retain
0x100343ce8 <+24>:  sub    x8, x29, #24              ; =24 
0x100343cec <+28>:  ldur   x30, [x29, #-32]
0x100343cf0 <+32>:  str    x0, [sp, #48]
0x100343cf4 <+36>:  mov    x0, x30
0x100343cf8 <+40>:  bl     0x100343da4               ; [appname].DFTransaction.baseAmount.getter : __C.Decimal at DFTransaction.swift:64
0x100343cfc <+44>:  ldur   w9, [x29, #-24]
0x100343d00 <+48>:  ldurh  w10, [x29, #-20]
0x100343d04 <+52>:  ldurh  w11, [x29, #-18]
0x100343d08 <+56>:  ldurh  w12, [x29, #-16]
0x100343d0c <+60>:  ldurh  w13, [x29, #-14]
0x100343d10 <+64>:  ldurh  w14, [x29, #-12]
0x100343d14 <+68>:  ldurh  w15, [x29, #-10]
0x100343d18 <+72>:  ldurh  w16, [x29, #-8]
0x100343d1c <+76>:  ldurh  w17, [x29, #-6]
0x100343d20 <+80>:  ldur   x0, [x29, #-32]
0x100343d24 <+84>:  str    w9, [sp, #44]
0x100343d28 <+88>:  str    w10, [sp, #40]
0x100343d2c <+92>:  str    w11, [sp, #36]
0x100343d30 <+96>:  str    w12, [sp, #32]
0x100343d34 <+100>: str    w13, [sp, #28]
0x100343d38 <+104>: str    w14, [sp, #24]
0x100343d3c <+108>: str    w15, [sp, #20]
0x100343d40 <+112>: str    w16, [sp, #16]
0x100343d44 <+116>: str    w17, [sp, #12]
0x100343d48 <+120>: bl     0x10051ca58               ; symbol stub for: objc_release
0x100343d4c <+124>: ldr    w9, [sp, #44]
0x100343d50 <+128>: ldur   x8, [x29, #-40]
->  0x100343d54 <+132>: str    w9, [x8]    //EXC_BAD_ACCESS
0x100343d58 <+136>: ldr    w10, [sp, #40]
0x100343d5c <+140>: strh   w10, [x8, #4]
0x100343d60 <+144>: ldr    w11, [sp, #36]
0x100343d64 <+148>: strh   w11, [x8, #6]
0x100343d68 <+152>: ldr    w12, [sp, #32]
0x100343d6c <+156>: strh   w12, [x8, #8]
0x100343d70 <+160>: ldr    w13, [sp, #28]
0x100343d74 <+164>: strh   w13, [x8, #10]
0x100343d78 <+168>: ldr    w14, [sp, #24]
0x100343d7c <+172>: strh   w14, [x8, #12]
0x100343d80 <+176>: ldr    w15, [sp, #20]
0x100343d84 <+180>: strh   w15, [x8, #14]
0x100343d88 <+184>: ldr    w16, [sp, #16]
0x100343d8c <+188>: strh   w16, [x8, #16]
0x100343d90 <+192>: ldr    w17, [sp, #12]
0x100343d94 <+196>: strh   w17, [x8, #18]
0x100343d98 <+200>: mov    sp, x29
0x100343d9c <+204>: ldp    x29, x30, [sp], #16
0x100343da0 <+208>: ret    

我在文章中提到 "Primitive accessors are always nullable Objective-C reference types"。这是一个不受 isOptional 设置影响的事实,正如您在这些崩溃中所发现的那样。

因此,原始访问器必须这样声明:

@NSManaged private var primitiveBaseAmount: NSDecimalNumber?

除此之外,您已将 baseAmount 访问器声明为 Decimal(非可选),因此您的自定义 get 实现必须在遇到一个 null 原始值,例如 return primitiveBaseAmount?.decimalValue ?? 0.