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

Object(value:) doesn't create unmanaged objects recursively #5818

Open
rogerluan opened this issue Jun 18, 2018 · 6 comments

Comments

@rogerluan
Copy link

commented Jun 18, 2018

Goals

I'd like to initialize an object using Object(value:) passing in an instance of Object, and as a result expect an unmanaged object with all of its properties (and their nested objects) unmanaged as well.

Expected Results

Obtain an unmanaged copy of the Object instance, with all of its child objects and relations being unmanaged (recursively).

Actual Results

It crashes when saving the newly created object. Why?
The new object is unmanaged, and its array (RLMArray) of other objects are also unmanaged, but the instances of that array are actually managed.

Steps to Reproduce

(I did this in Objective-C):
Create a new Object class, subclassing RLMObject, and another OtherObject class, also subclassing RLMObject. Object must contain a RLMArray of OtherObjects, complying with the OtherObject protocol (RLM_ARRAY_TYPE(OtherObject);).

Populate the database with sample data, including adding objects to the RLMArray<OtherObject><OtherObject *> otherObjects array.

(I did this in Swift):

let newObject = Object(value: object) // object is a managed object
print(newObject.realm) // nil
print(newObject.otherObjects) // nil
print(newObject.otherObjects.firstObject()!) // non-nill, it actually prints its Realm. Expected: nil

Code Sample

let newObject = Object(value: object) // object is a managed object
print(newObject.realm) // nil
print(newObject.otherObjects.realm) // nil
print(newObject.otherObjects.firstObject()!.realm) // non-nill, it actually prints its Realm. Expected: nil

Version of Realm and Tooling

ProductName:	Mac OS X
ProductVersion:	10.13.5
BuildVersion:	17F77

/Applications/Xcode.app/Contents/Developer
Xcode 9.4
Build version 9F1027a

/usr/local/bin/pod
1.5.3
Realm (3.1.0)
Realm (~> 3.1.0)
/** But I actually tested on Realm (3.7.2) (~> 3.7.0) and this bug is still present there! **/

/bin/bash
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin17)

carthage not found
(not in use here)

/usr/bin/git
git version 2.15.1 (Apple Git-101)
@christoff-1992

This comment has been minimized.

Copy link

commented Sep 11, 2018

Also just come across this issue, anything that I can assist with or is this in hand? @rogerluan have you worked around this for the time been?

@rogerluan

This comment has been minimized.

Copy link
Author

commented Sep 11, 2018

@christoff-1992 Luckily, in my use case, I actually didn't need the RLMArray of objects cloned, so I just cleared them (i.e. removeAllObjects()) and it worked fine.
If you do need them, though, you'd have to remove all objects from Object, clone them manually (i.e. using YourNestedObject(value:) for each object of the array), and then re-populate the array of the newly cloned object.
Lemme know if that works 👍

@christoff-1992

This comment has been minimized.

Copy link

commented Sep 12, 2018

Thanks, @rogerluan unfortunately for myself removing all the objects won't be an option. I am creating an unmanaged copy of the object as there is no undo functionality in Realm. The workaround will work but I have the joys of many nested objects 🤣. @tgoyne / @bmunkholm is this the expected behaviour when copying an object using Object(value:) or is this a bug? Thanks.

@tgoyne

This comment has been minimized.

Copy link
Member

commented Sep 12, 2018

Object(value:) performing a shallow copy is an unfortunate design decision and not a bug.

The main problem with changing it is that it's a breaking change: it's unlikely that anyone specifically wants to be accessing managed objects from their standalone copy of the parent, but if they aren't actually using those properties then changing to a deep copy would make what's currently a fairly cheap operation into something potentially very slow and memory hungry. It's not that rare to have a schema where every object is reachable from every other object, which means that Object(value:) would suddenly start copying the entire Realm into memory whenever it's used.

That said, we do clearly need a way to perform a deep copy out of a Realm. Early on we were wary about building something because it looked like it'd be a footgun that lead people in the wrong direction, but that hasn't really been a concern for years.

@rogerluan

This comment has been minimized.

Copy link
Author

commented Sep 12, 2018

Gotcha, that makes sense @tgoyne . Could that be solved by passing an optional parameter to the copy function? Sounds like that would solve the issue and it'd be up to the user if they explicitly want to perform a shallow copy or not.

@tgoyne

This comment has been minimized.

Copy link
Member

commented Sep 12, 2018

A potentially very simple solution would be a max depth parameter, which would also sorta let us punt on dealing with object cycles. Might have odd implications when the value isn't a Realm object, though? If you pass in a dictionary we have to fully recur and convert nested values to Realm objects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.