最近在项目中偶然发现了一个问题,那就是 Swift 的 CoreData 在 32 位系统下与 64 位系统下表现不一致的问题。
简单的说:如果你的 CoreData 模型有一个声明为 Boolean 的 Attribute,并且在代码中使用 NSNumber 来包装(而不是 Bool)的话,很可能会遇到这个问题。
这个问题简述之则是这样:
假如有一个 Post 的 CoreData 类型,它有一个 isPublished 的属性,CoreData 使用 NSNumber 来包装这个属性。我对其进行赋值:
post.isPublished = true
没错,虽然 isPublished 在代码中是 NSNumber 类型,但是得益于 Swift 的「Literal Convertibles」机制,我们可以直接给 NSNumber 赋值 true,然后它就会以 true 存储。
问题就在这里出现了。如果是在 32 位的系统下,我用
if post.isPublished == true {
}
进行条件判断,那么很遗憾不会走进这个条件分支里,在 64 位系统下是正常的。
如果改成:
if post.isPublished == 1 {
那么无论在 32 位和 64 位系统下都是正常的。
发现这个问题的,开始想解决方法,假设这真的是 Swift 在 32 位系统下的 Bug,难道我要把这些比较都改成 1 或 0?
后来我找到一个方法,那就是创建 CoreData 的 NSManagedObject 的 class 的时候,勾上那个 Option:Use scalar properties for primitive data types,这样 isPublished 就不是用 NSNumber 这种包装型的,而是直接用 Bool 类型了。经测试,无论在 32 位系统下还是 64 位系统下,条件判断都工作正常了。算是优雅地绕开了这个可能是 Swift 的 Bug。
后来我又尝试了下不用 CoreData,直接用 NSNumber = false 的形式来进行判断,发现没有这个问题。看来这个问题可能只存在 CoreData 上。
既然写起了 Swift + CoreData 这个组合,免不了需要吐槽一个 Apple 做的还不好的地方:
以往 Objective-C 项目时,CoreData 对象的一个属性是不是空值,我们直接判断是不是 nil 就可以了,但是在 Swift 项目下,一切变得麻烦了,Xcode 默认给我们产生的 CoreData 对象的属性,全都不是optional 的,也就是说,如果一个属性可能是空值,我们还要手动给这个加上一个「?」,它才会如我们所愿可以用 Optional 的方式。这点实在是很不方便。
具体可以看一个帖子:Swift + CoreData: Cannot Automatically Set Optional Attribute On Generated NSManagedObject Subclass
OK,希望接下去不要碰到太多类似的坑。