最近在項目中偶然發現了一個問題,那就是 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,希望接下去不要碰到太多類似的坑。