Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RealmSwiftObject defaultPropertyValues]: unrecognized selector sent to class #4828

Closed
levibostian opened this issue Apr 6, 2017 · 9 comments
Assignees
Labels

Comments

@levibostian
Copy link

levibostian commented Apr 6, 2017

I am attempting to create an unmanaged instance of a managed Realm Object.

let managedFooTask: PendingTask = realm.getFooTaskById(10) as! PendingTask
// Above is my managed task I got from the realm, below I am trying to get an unmanaged instance of it.
let fooUnmanagedTask: Object = Object(value: managedFooTask as! Object)

This is my Realm Object model for FooTask:

class FooTask: Object, PendingTask {
    
    convenience init(realmId: Int) {
        self.init()
        self.realmId = realmId
    }
    
    dynamic var realmId: Int = 0
    dynamic var createdAt: Date = Date()
    dynamic var manuallyRunTask: Bool = false
    
    override static func primaryKey() -> String? {    
        return "realmId"
    }
    
    func queryForExistingTask() -> NSPredicate {
        return NSPredicate(format: "realmId == \(realmId)")
    }
    
    func canRunTask(realm: Realm) -> Bool {
        return true
    }    
}

The crash happens on line:

let fooUnmanagedTask: Object = Object(value: managedFooTask as! Object)

If you notice in the Realm model that I have created, FooTask there are default values given for all of the properties. Would that be the issue with this crash?

Goals

Create an unmanaged instance of a managed Realm Object.

Expected Results

Get an unmanaged instance of a managed Realm Object

Actual Results

2017-04-06 16:33:36.269 - Debug[76856:8863377] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[RealmSwiftObject defaultPropertyValues]: unrecognized selector sent to class 0x10711c0f8'

Steps to Reproduce

Create an instance of FooTask somewhere in the app on a background thread. After the Realm write block is complete, send a notification via NSNotification. AppDelegate receives notification by observing it and then runs the code snippet given above in a new background thread where I am (1) querying Realm to get my managed instance of FooTask from the realm and (2) create an unmanaged instance of it.

My managed realm object is valid as I set a breakpoint before: let fooUnmanagedTask: Object = Object(value: managedFooTask as! Object) and po managedFooTask to get a description of the object with class name FooTask and all properties populated with the correct values.

I have read this issue which seems similar and what also told me to use Object(value: ) to create an unmanaged instance of a managed object as well as this issue which gave me the hint to try and clean the project to see if that would fix this issue. I cleaned out all of my pods via rm -rf Pods/ and reinstalled them all via pod update, then performed an Xcode Clean, ran the app again and same exception.

Version of Realm and Tooling

  • Realm framework version: 2.5.1
  • Xcode version: 8.3
  • iOS/OSX version: iOS simulator iPhone 7 version 10.3
@karapigeon
Copy link

Hi @levibostian. Thanks for reaching out about this. I wanted to let you know that we've received your issue and that someone will follow-up with you soon.

@levibostian
Copy link
Author

Thank you @istx25

@jpsim
Copy link
Contributor

jpsim commented Apr 10, 2017

The first thing that jumps out at me is that the Object class isn't intended to be used directly, but rather as a parent class to subclass for your own models.

If you refactor your code to avoid that, do you still hit this issue? If so, let me know ideally with a sample Xcode project, and I'll take a closer look.

@levibostian
Copy link
Author

I do understand that Object is supposed to not be used directly. That is fine. I am all about best practices. I can use another method besides creating an Object instance directly.

I got the idea to creating an Object instance from your comment in this issue.

Is there another way to create an unmanaged instance from a managed instance of an object? I come from the realm-java lib where creating unmanaged instances of managed instances is built into the lib. What is the realm-cocoa equivalent or best practice instead?

My use case: I perform a realm query to get a realm Object > I perform a write on this object > Perform an API call > Perform another write on this object.

I am trying to create an unmanaged object because I can share the object across threads (as I will be on a different thread after the API call is performed since the API call will be performed on another thread and the callback after it is complete will be called from this API thread).

I was looking through the Realm docs yesterday for Swift and it mentioned passing instances across threads. I am guessing this is the realm-cocoa equivalent to working with managed objects across threads?

I don't mind this solution. However, it does not seem as flexible as creating an unmanaged object. Unmanaged objects are very flexible do handle them however you would like. The biggest limitation that I see is A ThreadSafeReference object must be resolved at most once. I may have scenarios that I would need to resolve a ThreadSafeReference 2+ times. What do I do in this case? Is there a pattern that users usually do?

@TimOliver
Copy link
Contributor

Hi @levibostian!

Like JP said, the Realm Object class is an abstract-like class that isn't intended to be used on its own. The schema that controls the types of data that the Object will store are defined in the user-defined Object subclasses. Object itself has no schema, so it has no idea how to persist those properties. But you can easily use the PendingTask subclass to create unmanaged copies.

To create an unmanaged version of PendingTask, it's completely fine to do this:

let managedFooTask = realm.getFooTaskById(10)
let fooUnmanagedTask = PendingTask(value: managedFooTask)

Creating unmanaged copies in order to pass them between threads would work, but it would be quite inefficient. You'd be paging in and making copies of each object you're working with, and you'd introduce the overhead of re-inserting the objects back into Realm as opposed to to simply updating the values you changed.

If you have a need for tracking objects across multiple threads, and you want to minimize the number of times you need to create a thread reference, the technique we used to recommend before then was to assign a primary key property to each object, pass the primary key value itself across threads, and then use realm.object(ofType:forPrimaryKey:) to re-query for that object on the new thread.

I hope that helped!

@levibostian
Copy link
Author

@TimOliver, that is super helpful. Super helpful.

I will give the PendingTask(value: managedFooTask) a shot for curiosity reasons. I do understand the overhead that you are talking about. I have been handling unmanaged objects in realm-java for a year now. Luckily I have variable naming on my side :)

I will look into realm.object(ofType:forPrimaryKey:) too.

If I decide to use PendingTask(value: managedFooTask) or realm.object(ofType:forPrimaryKey:), I have never gotten it to work completely. I am never creating direct instances of PendingTask(). I am subclassing it. PendingTask() is a protocol.

So, I have been trying to do generic realm queries with no success. I understand generics. Been using them in Java for years. Swift is a little different, however (and not as complete as Java in terms of functionality) and have had issues getting it to work.

Any tips on generic realm queries? Being able to query realm for a generic type (generic subclass of Object and PendingTask).

@TimOliver
Copy link
Contributor

Hi @levibostian!

Cool! Give it a try and let us know how you go!

Hmm, generic queries? How would that look exactly?

In terms of what I think you're hoping for, Realm doesn't do generic queries, sorry. When you perform a query in Realm, you specify the exact Object class you wish to query, and the resulting Results object will represent a collection of those subclass model objects.

There's been some discussion about methods of applying polymorphic practices to Realm objects. I'd recommend checking that out if it's something you're curious about adopting. :)

@levibostian
Copy link
Author

That's what I needed. Thank you. I have learned what I needed. Thank you so much for all the help, team.

@TimOliver
Copy link
Contributor

Awesome! Glad I was able to help! Thanks a lot! :)

Best of luck!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Mar 15, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

5 participants