【问题标题】:Core Data custom accessor crash when saving context with nil required attribute使用 nil required 属性保存上下文时,Core Data 自定义访问器崩溃
【发布时间】:2017-01-23 22:12:28
【问题描述】:

在阅读 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    

【问题讨论】:

    标签: ios swift core-data magicalrecord


    【解决方案1】:

    在文章中我提到“原始访问器总是可以为空的 Objective-C 引用类型”。这是一个不受 isOptional 设置影响的事实,正如您在那些崩溃中发现的那样。

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

    @NSManaged private var primitiveBaseAmount: NSDecimalNumber?

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

    【讨论】:

    • 感谢您的快速输入。我认为可能是这种情况,但我不确定是否有可能以另一种方式处理。
    • 实际上,我刚刚实施了您的更改,但仍然看到完全相同的崩溃。我认为这可能与我对 MagicalRecord 的使用以及 Swift 3 中缺少与 Objective-C 类型的自动桥接有关。如果我不能很快解决这个问题,我可能会回滚到 NSDecimalNumber 并完成它。
    • 问题的根源可能在于CoreData 不喜欢Decimal 的标量访问器,即使Decimal 属性可以声明为@objc。在我的文章中,我谈到了如何声明这些访问器“不会破坏 KVC”,但在 Decimal 访问器的情况下,KVC 肯定会被破坏。也许这就是这里的工作。将您的自定义访问器声明为 @nonobjc var baseAmount: Decimal { get { ... } set { ... } } 并查看它是否解决了问题。
    • 这很有趣,虽然我怕我已经没有勇气继续使用Decimal;我已经回到NSDecimalNumber。感觉有些东西总是会坏掉,对我来说,仅仅拥有更清洁的类型标签已经不值得了。感谢您的帮助,我相信这对未来更勇敢的灵魂会有所帮助。
    猜你喜欢
    • 2023-04-04
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-17
    相关资源
    最近更新 更多