realm / realm-swift

Realm is a mobile database: a replacement for Core Data & SQLite
https://realm.io
Apache License 2.0
16.31k stars 2.14k forks source link

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Swift._SwiftDeferredNSArray objectClassName]: unrecognized selector sent to instance 0x608000037720' #2720

Closed bipinvaylu closed 9 years ago

bipinvaylu commented 9 years ago

Exception logs:

2015-10-21 14:55:34.914 ottodj[59456:1643376] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Swift._SwiftDeferredNSArray objectClassName]: unrecognized selector sent to instance 0x608000037720'
*** First throw call stack:
(
    0   CoreFoundation                      0x00007fff9268c03c __exceptionPreprocess + 172
    1   libobjc.A.dylib                     0x00007fff8879e76e objc_exception_throw + 43
    2   CoreFoundation                      0x00007fff9268f0ad -[NSObject(NSObject) doesNotRecognizeSelector:] + 205
    3   CoreFoundation                      0x00007fff925d4e24 ___forwarding___ + 1028
    4   CoreFoundation                      0x00007fff925d4998 _CF_forwarding_prep_0 + 120
    5   Realm                               0x00000001003c4952 -[RLMProperty initSwiftPropertyWithName:indexed:property:instance:] + 514
    6   Realm                               0x000000010038c266 +[RLMObjectSchema propertiesForClass:isSwift:] + 1174
    7   Realm                               0x000000010038a5e2 +[RLMObjectSchema schemaForObjectClass:] + 818
    8   Realm                               0x000000010049484f +[RLMSchema registerClasses:count:] + 1871
    9   Realm                               0x0000000100496625 _ZZ25+[RLMSchema sharedSchema]ENK3$_0clEv + 501
    10  Realm                               0x0000000100496425 _ZNSt3__117__call_once_proxyINS_5tupleIJZ25+[RLMSchema sharedSchema]E3$_0EEEEEvPv + 85
    11  libc++.1.dylib                      0x00007fff8b177332 _ZNSt3__111__call_onceERVmPvPFvS2_E + 117
    12  Realm                               0x0000000100494edd +[RLMSchema sharedSchema] + 413
    13  Realm                               0x000000010038680b +[RLMObjectBase sharedSchema] + 43
    14  Realm                               0x0000000100383f34 -[RLMObjectBase init] + 244
    15  Realm                               0x0000000100381bdb -[RLMObject init] + 59
    16  ottodj                              0x0000000100029dd7 _TFC6ottodj19ServerTrackMetadatacfMS0_FT_S0_ + 215
    17  ottodj                              0x00000001000273f1 _TFC6ottodj19ServerTrackMetadatacfMS0_FT7cueTypeOS_4Type7cueTimeSd9createdBySi10apiVersionSi6hotcueSi_S0_ + 97
    18  ottodj                              0x00000001000274fa _TFC6ottodj19ServerTrackMetadataCfMS0_FT7cueTypeOS_4Type7cueTimeSd9createdBySi10apiVersionSi6hotcueSi_S0_ + 122
    19  ottodj                              0x000000010000e045 _TFC6ottodj20SignInViewController13viewDidAppearfS0_FT_T_ + 6549
    20  ottodj                              0x000000010000e1b2 _TToFC6ottodj20SignInViewController13viewDidAppearfS0_FT_T_ + 34
    21  AppKit                              0x00007fff90620f8e -[NSViewController _sendViewDidAppear] + 160
    22  AppKit                              0x00007fff90576cae -[NSView(NSInternal) _windowDidOrderOnScreen] + 67
    23  AppKit                              0x00007fff90576d90 -[NSView(NSInternal) _windowDidOrderOnScreen] + 293
    24  AppKit                              0x00007fff90564c26 -[NSWindow _reallyDoOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 3915
    25  AppKit                              0x00007fff90563787 -[NSWindow _doOrderWindow:relativeTo:findKey:forCounter:force:isModal:] + 829
    26  AppKit                              0x00007fff905633db -[NSWindow orderWindow:relativeTo:] + 159
    27  AppKit                              0x00007fff9055a9a2 -[NSWindow makeKeyAndOrderFront:] + 47
    28  AppKit                              0x00007fff9063de27 __33-[NSWindowController showWindow:]_block_invoke + 367
    29  QuickLookUI                         0x00007fff88bce5f6 -[QLSeamlessDocumentOpener showWindow:contentFrame:withBlock:] + 105
    30  AppKit                              0x00007fff905f3d60 -[NSWindowController showWindow:] + 434
    31  AppKit                              0x00007fff9042ff54 NSApplicationMain + 1080
    32  ottodj                              0x00000001000617d7 main + 87
    33  libdyld.dylib                       0x00007fff9220e5c9 start + 1
    34  ???                                 0x0000000000000003 0x0 + 3

Model code is like:

public class ServerTrackMetadata: RLMObject {
    dynamic var id = 0
    dynamic var cueType = ""
    dynamic var cueTime = 0.0
    dynamic var createdBy = 0
    dynamic var hotcue = -1
    dynamic var apiVersion = 0
    dynamic var createdAt = 0
    dynamic var updatedAt = 0
    public convenience init(cueType: Type, cueTime: Double, createdBy: Int, apiVersion: Int, hotcue:Int) {
        self.init() //Getting error at this line
        self.cueType = cueType.rawValue
        self.cueTime = cueTime
        self.createdBy = createdBy
        self.apiVersion = apiVersion
        self.hotcue = hotcue
    }
    public convenience init(metadata: JSON) {
        self.init() //Getting error at this line
        if  let id = metadata["id"].int,
            let cueType = metadata["cueType"].string,
            let cueTime = metadata["cueTime"].double,
            let createdBy = metadata["createdBy"].int,
            let apiVersion = metadata["apiVersion"].int,
            let createdAt = metadata["createdAt"].int,
            let updatedAt = metadata["updatedAt"].int {
                self.id = id
                self.cueType = cueType
                self.cueTime = cueTime
                self.createdBy = createdBy
                self.apiVersion = apiVersion
                self.createdAt = createdAt
                self.updatedAt = updatedAt
        }
    }

    override public static func primaryKey() -> String? {
        return "id"
    }

Just initialising model

let trackMetadata = ServerTrackMetadata(cueType: Type.Start, cueTime: 1234, createdBy: 1234, apiVersion: 1, hotcue: 123)
mrackwitz commented 9 years ago

I don't think that the specific model and the crash are directly related. From the stack trace, I'm pretty sure It's caused by initializing your first model instance, which triggers the lazy-loading of the schema. It seems like you defined a property as Swift array e.g. linkedObjects = [Foo](), which doesn't work for several reasons. Instead you've to define your to-many relationships with the provided List-type e.g. linkedObjects = List<Foo>(). Is my assumption correct and does that help?

bipinvaylu commented 9 years ago

Thanks @mrackwitz i am having 2 main models ServerTrack and ServerTrackMetadata. ServerTrack is having array of ServerTrackMetadata objects. Currently i am trying to create single object of ServerTrackMetadata but it's still giving same error. I checked stuff with sample app that's working fine. Realm Sample app: https://github.com/bipinvaylu/RealmDemo.

bipinvaylu commented 9 years ago

@mrackwitz i copied models and it's stuff from RealmDemo project to main project, it's also creating issue in main project. Looks like something in settings. Please let me know if you need any other details. Does it something related to build settings? I already did Install Objective-C Compatibility Header = No. Model: https://github.com/bipinvaylu/RealmDemo/blob/master/RealmDemo/Model.swift

FYI: i am using Swift and Objective-C for my project. Imported Realm.h in main-Bridging-Header.h

bipinvaylu commented 9 years ago

Here is my actual model structure:

/Users/Krunal/Library/Developer/Xcode/DerivedData/ottodj-asziciqqpxunbeazllcntqycmszd/Build/Products/Debug/ottodj.app/Contents

public class ServerTrack: RLMObject {

    dynamic var id = 0
    dynamic var traktorAudioId = ""
    dynamic var bpm = 0.0
    dynamic var metadata = RLMArray(objectClassName: ServerTrackMetadata.className())
    dynamic var createdAt = 0
    dynamic  var updatedAt = 0

    public convenience init(traktorAudioId: String, bpm: Double, metadata: [ServerTrackMetadata]) {
        self.init()
        self.traktorAudioId = traktorAudioId
        self.bpm = bpm
        self.metadata.addObjects(metadata)
    }

    public convenience init(track: JSON) {
        self.init()
        if  let id = track["id"].int,
            let traktorAudioId = track["traktorAudioId"].string,
            let bpm = track["bpm"].double,
            let createdAt = track["createdAt"].int,
            let updatedAt = track["updatedAt"].int,
            let metadataJSONArray = track["metadata"].array {
                self.id = id
                self.traktorAudioId = traktorAudioId;
                self.bpm = bpm
                self.createdAt = createdAt
                self.updatedAt = updatedAt
                for metadataJSON in metadataJSONArray {
                    let metadata: ServerTrackMetadata = ServerTrackMetadata(metadata: metadataJSON) as RLMObject
                    self.metadata.addObject(metadata)
                }
        }
    }
}

public class ServerTrackMetadata: RLMObject {

    dynamic var id = 0
    dynamic var cueType = ""
    dynamic var cueTime = 0.0
    dynamic var createdBy = 0
    dynamic var hotcue = -1
    dynamic var apiVersion = 0
    dynamic var createdAt = 0
    dynamic var updatedAt = 0

    public convenience init(cueType: String, cueTime: Double, createdBy: Int, apiVersion: Int, hotcue:Int) {
        self.init()
        self.cueType = cueType
        self.cueTime = cueTime
        self.createdBy = createdBy
        self.apiVersion = apiVersion
        self.hotcue = hotcue
    }

    public convenience init(metadata: JSON) {
        self.init()
        if  let id = metadata["id"].int,
            let cueType = metadata["cueType"].string,
            let cueTime = metadata["cueTime"].double,
            let createdBy = metadata["createdBy"].int,
            let apiVersion = metadata["apiVersion"].int,
            let createdAt = metadata["createdAt"].int,
            let updatedAt = metadata["updatedAt"].int {

                self.id = id
                self.cueType = cueType
                self.cueTime = cueTime
                self.createdBy = createdBy
                self.apiVersion = apiVersion
                self.createdAt = createdAt
                self.updatedAt = updatedAt
        }
    }
}

Can you please help me figure out this issue.

bipinvaylu commented 9 years ago

I am getting this error because of this code: https://github.com/bipinvaylu/RealmDemo/blob/master/RealmDemo/ServerTrack.swift #178, Can anyone please help me figure out this issue.

bipinvaylu commented 9 years ago

@mrackwitz @jpsim @realm-ci I figured this issue, it's because of i am having function named getMetadata() in ServerTrackMetadata. One thing i want to tell you guys is that error message is too much weird, No one can predict this can be issue. I spent around 3-4 days to figure out this weird stuff. :disappointed:

mrackwitz commented 9 years ago

Sorry @bipinvaylu for causing you additional inconvenience in the last day, I totally own this. I'm currently abroad and had thus restricted internet access due to travel the last day. I missed to handover your issue to one of my team mates.

The sample project you provided was actually a pretty great way to reproduce this issue, but before that it was really cryptic and I don't think anyone of us has came across such an edge case yet and would have been able to recognize just from the exception message or the limited model you shared first with us what is going on here.

You're right that exception thrown here is not helpful at all, but that's just because that wasn't really handled yet at all.

Let me explain what is happening here: Realm's RLMObject uses the Objective-C runtime to define dynamic accessors for your model properties to provide own getter and setter implementation. As Swift doesn't allow it to declare a type which is conforming to a protocol as Objective-C does it with RLMArray<Foo>, we've to rely there on a property objectClassName, which you correctly initialized. On runtime when you're initializing the first RLMRealm instance, the schema is loaded by inspecting your classes via the reflection APIs. Because objectClassName is a field of the RLMArray property, classes with to-many relations which are defined from Swift have to be initialized at runtime to be able to inspect them. Realm Objective-C is accessing the value for the property via valueForKey:. KVC is accessing values by first looking for an accessor method which matches the pattern -get<Key>. This is the one you have overwritten here. By returning a standalone array instead of the RLMArray, you are accidentally breaching the class contract for RLMObject. The schema inspection code is trying to figure out the element type by calling objectClassName on the returned value which is in that case the toll-free bridged _SwiftDeferredNSArray. This doesn't know anything about Realm, so it comes to the failure.

bipinvaylu commented 9 years ago

Great thanks @mrackwitz for explanation. I got it now.