使用 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
.
阅读 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
.