日期。\u在Swift 3中无条件地从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

我在swift文件中有以下功能。我用NSDate代替startDate从Obj C文件中调用它。而且,我的应用程序经常(并非每次)因以下原因崩溃:

日期。\u无条件地从ObjectiveC(NSDate?)跨越

我怎样才能解决这个问题

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中将属性表示为可选属性,您仍应使用将属性标记为非可选属性的功能。这在核心数据的对象验证代码中得到考虑。

如果仍有人试图解决此问题:

  • 确保在Swift中,指定的值是可选的
  • 确保ObjC类中的属性标记为
    nullable
  • i、 e

    ObjC

    以迅捷的速度:

    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