Swift 3中的Date._unconditionallyBridgeFromObjectiveC(NSDate?)崩溃

时间:2017-05-17 17:54:03

标签: date swift3 nsdate

我在swift文件中有以下功能。我用Obj C文件用NSDate代替startDate来调用它。而且很多时候,不是每次,我的应用程序崩溃

  

Date._unconditionallyBridgeFromObjectiveC(的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之后才能看到这个问题。围绕此问题是否有任何已知问题?我在网上找不到任何东西:(

4 个答案:

答案 0 :(得分:6)

您可能需要仔细检查来自Objective-C的NSDate是否实际上是非nil,因为没有任何内容可以在Obj-上积极执行C侧是Swift方面的方式。

我尽可能多地将Date转换为Date? s,因为我发现从Obj-C调用,然后进行了大量的guard let检查。

当您正在开发有关您未预料到的assertionFailure日期来自何处时,您还可以向nil发号施令对您大吼大叫。例如:

guard let date = passedInDate else {
    assertionFailure("Turns out the passed-in date was nil!")
    return 
}

然后看看你的堆栈跟踪,看看你是否可以更好地掌握为什么你会在那里获得意外的nil值。

更新:Here&#39}在Swift源代码中发生崩溃的地方。

答案 1 :(得分:0)

此答案适用于那些面临Core Data和Swift问题的人:

在Core Data NSManagedObject中,在Swift的数据模型中表示标记为非可选的类型时,您必须要小心。

核心数据对象本质上是动态的,并且内存中的值是通过设计在运行时动态实现的。除非您在数据模型中定义默认值,否则这与Swift的真正非可选类型的概念不兼容。

请注意,即使在数据模型中将该属性标记为非可选的情况下,默认情况下,自动生成的类也总是求助于可选的。 要正确支持非可选选项,您必须在模型中定义一个默认值

您可能会认为这是Apple的错误,但事实并非如此。可以删除Core Data对象,并且您仍然可以在代码中的某个位置对其进行引用(请参见isDeleted属性),因此标记为非可选的内容可以在运行时消失,因为Core Data永远无法满足其动态期望。

例如,StringInt崩溃时,0nil将“无条件地桥接”到“”和Date这个问题。

请注意,如果您将其标记为NSDate(即使标记为非可选,它将返回nil),情况并非如此,但这并不是解决方法,而是实现的详细信息。

测试用例:

假设您的数据模型具有date1date2,均为非可选且没有默认值。

@NSManaged public var date1: Date?
@NSManaged public var date2: Date
let myObject = ... 
// Assuming your object is valid, inserted, and the context is saved              
managedObjectContext.delete(myObject)
try managedObjectContext.save()
// This will be nil
NSLog("\(myObject.date1)")
// This will crash
NSLog("\(myObject.date2)")

总而言之,您可以在Core Data中使用没有默认值的非可选类型,请小心,因为在运行时,它们可能在Date的情况下消失并崩溃,或者以您不期望的方式表示字符串和整数的情况。

注意:即使在Swift中将属性表示为可选,您仍应使用将属性标记为非可选的功能。 Core Data的对象验证代码已考虑到这一点。

答案 2 :(得分:0)

如果有人还在想办法解决这个问题:

  1. 确保在 Swift 中,您分配的值是可选的;
  2. 确保ObjC 类中的属性标记为nullable

对象

NS_ASSUME_NONNULL_BEGIN

@interface Item : NSObject

@property (nonatomic, nullable) NSDate *imageURL;

@end

NS_ASSUME_NONNULL_END

在 Swift 中:

var imageURL: Date? = itme.imageURL

这将解决问题。 如果你没有在 ObjC 中将该属性标记为可空,Swift 会假设它不能为 nil,尽管你正确地将它声明为 Date?。这将导致标题中提到的崩溃。 在 ObjC 中添加 nullable 后,崩溃将得到解决。

答案 3 :(得分:0)

为 swift & coreData + 多线程环境扩展 Marc Etcheverry 答案。

请注意,NSFetchRequest 有一个名为 returnsObjectsAsFaults 的属性,默认情况下为 true (apple doc) 这意味着您将检索您的 NSManagedObject 但它的属性在您访问它们之前实际上不会被填充,因此,如果在您访问该属性(在本例中为日期 Date._unconditionallyBridgeFromObjectiveC)时,其他线程万一从核心数据中删除了该实例,它可以为零而您的非可选 Date 可能会导致应用崩溃。

想象一下这个场景:

Thread 1 

 - [step 1] let values = Fetch [NSManagedObject]

 - [step 3] let values[0].someDate // here app will crash

因为 returnsObjectsAsFaultstrue [默认] someDate 属性在您尝试访问它之前不会被提取。

Thread 2 

 - [step 2] update coreData - hence removing objects fetched on Thread 1 - step 1

解决方案:如果您确实需要从 NSManagedObject 开始的所有属性,请确保在您的 fetchRequest 上进行设置:

let request = NSFetchRequest<T>(entityName: "someEntity")
...
request.returnsObjectsAsFaults = false