日期。\u在Swift 3中无条件地从ObjectiveC(NSDate?)崩溃
我在swift文件中有以下功能。我用NSDate代替startDate从Obj C文件中调用它。而且,我的应用程序经常(并非每次)因以下原因崩溃: 日期。\u无条件地从ObjectiveC(NSDate?)跨越 我怎样才能解决这个问题日期。\u在Swift 3中无条件地从ObjectiveC(NSDate?)崩溃,date,swift3,nsdate,Date,Swift3,Nsdate,我在swift文件中有以下功能。我用NSDate代替startDate从Obj C文件中调用它。而且,我的应用程序经常(并非每次)因以下原因崩溃: 日期。\u无条件地从ObjectiveC(NSDate?)跨越 我怎样才能解决这个问题 func trackMeetingEnded(_ name: String, startDate: Date, backgroundTime: TimeInterval) {} 堆栈跟踪 Crashed: com.apple.main-thread 0 lib
func trackMeetingEnded(_ name: String, startDate: Date, backgroundTime: TimeInterval) {}
堆栈跟踪
Crashed: com.apple.main-thread
0 libswiftFoundation.dylib 0x102061e98 static Date._unconditionallyBridgeFromObjectiveC(NSDate?) -> Date + 72
1 Acid 0x10017ece4 @objc static ClusteredMixpanel.trackMeetingEnded(String, startDate : Date, backgroundTime : Double) -> () (MixpanelMeeting.swift)
2 Acid 0x10073e1bc __56-[MeetingLifeCycleViewController stateInitialization]_block_invoke.221 (MeetingLifeCycleViewController.m:267)
3 Acid 0x1001ee5c4 partial apply for thunk (StateMachine.swift)
4 Acid 0x1001ea70c specialized State.willLeaveState(State) -> () (StateMachine.swift:238)
5 Acid 0x1001ead90 specialized StateMachine.transitionToState(State) -> Bool (StateMachine.swift)
6 Acid 0x1001e1f18 @objc StateMachine.transitionToState(State) -> Bool (StateMachine.swift)
7 Acid 0x10073ace0 -[MeetingLifeCycleViewController dismissCall] (MeetingLifeCycleViewController.m:538)
8 Acid 0x10086d648 -[InMeetingViewController hangup] (InMeetingViewController.m:531)
enter code here
我相信在这种情况下,NSDate-to-Date转换是由操作系统完成的。只有在迁移到Swift 3后才能看到该问题。这方面有什么已知的问题吗?我在网上找不到任何东西:(您可能需要再次检查来自Objective-C的
NSDate
是否实际上是非nil
,因为在Obj-C端没有任何东西像Swift端那样积极地强制执行
最后,我将Obj-C调用的所有Date
s转换为Date?
s,然后进行了大量的guard let
检查
您还可以在开发过程中加入一个assertionFailure
,对您大喊大叫,告诉您您不期望的nil
日期来自何处。例如:
guard let date = passedInDate else {
assertionFailure("Turns out the passed-in date was nil!")
return
}
然后,当堆栈跟踪被命中时,查看一下堆栈跟踪,看看您是否能够更好地掌握为什么会在那里得到意外的nil
值
更新:Swift源代码中发生崩溃的地方。此答案适用于那些面临核心数据和Swift问题的人: 在核心数据
NSManagedObject
中,在Swift中表示数据模型中标记为非可选的类型时,必须小心
核心数据对象本质上是动态的,内存中的值通过设计在运行时动态实现。这与Swift的真正非可选类型概念不兼容,除非您在数据模型中定义默认值
请注意,默认情况下,自动生成的类始终使用可选属性,即使该属性在数据模型中标记为非可选。要正确支持非可选属性,必须在模型中定义默认值
你可能认为这是苹果方面的一个错误,但事实并非如此。核心数据对象可以被删除,并且你仍然可以在代码中的某个地方引用它(请参见isDeleted
属性),因此标记为非可选的内容可以在运行时消失,因为核心数据永远无法满足其动态预期
例如,String
和Int
将在nil
时“无条件桥接”到“”和0
,Date
将崩溃,如本问题所示
请注意,如果将其标记为NSDate
(即使标记为非可选,它也将返回nil),则情况并非如此,但这并不是解决方法,而是实现细节
测试用例:
假设您的数据模型有date1
和date2
,这两个值都是非可选的,并且没有默认值
@NSManaged public var date1: Date?
@NSManaged public var date2: Date
总之,您可以在核心数据中使用不带默认值的非可选类型,但要小心,因为在运行时,它们可能会在Date的情况下消失并崩溃,或者在String和Int的情况下以您不期望的形式表示
注意:即使在Swift中将属性表示为可选属性,您仍应使用将属性标记为非可选属性的功能。这在核心数据的对象验证代码中得到考虑。如果仍有人试图解决此问题:
nullable
var imageURL: Date? = itme.imageURL
这将解决问题。
如果在ObjC中未将该属性标记为可空,Swift将假定该属性不能为零,尽管您正确地将其声明为Date?
。这将导致标题中提到的崩溃。
在ObjC中添加nullable
后,崩溃将得到解决。扩展swift&coreData+多线程环境的答案
请注意,NSFetchRequest
有一个名为returnsObjectsAsFaults
的属性,默认情况下该属性为true
)这意味着您将检索NSManagedObject
,但在您访问它们之前,它的属性实际上不会被填充,因此,如果在您访问属性的那一刻,另一个线程从核心数据中删除了该实例(本例中的日期为Date.\u无条件地从ObjectiveC中删除该实例
)它可以是零,并且您的非可选日期可以使应用程序崩溃
想象一下这个场景:
Thread 1
- [step 1] let values = Fetch [NSManagedObject]
- [step 3] let values[0].someDate // here app will crash
由于returnsObjectsAsFaults
为true
[默认情况下]someDate
属性在您尝试访问它之前未被获取
Thread 2
- [step 2] update coreData - hence removing objects fetched on Thread 1 - step 1
解决方案:如果您确实需要从NSManagedObject开始的所有属性
请确保在您的请求中设置:
let request = NSFetchRequest<T>(entityName: "someEntity")
...
request.returnsObjectsAsFaults = false
let request=NSFetchRequest(entityName:“someEntity”)
...
request.returnsObjectsAsFaults=false
粘贴您的函数或代码我必须将startDate设置为可选。结果是传递了nil。我使用的是带有默认值的非可选函数,在处理持久历史记录时,我仍然很少遇到此崩溃。您没有指定它是哪种类型。通常,在Swift中,将非可选函数与NSManagedObject一起使用不是一个好主意,即使您设置了默认值。核心数据无法实现该属性的原因有很多。这可能失败的“许多”其他原因是什么?
Thread 2
- [step 2] update coreData - hence removing objects fetched on Thread 1 - step 1
let request = NSFetchRequest<T>(entityName: "someEntity")
...
request.returnsObjectsAsFaults = false