Realm-cocoa: Object(value:) doesn't create unmanaged objects recursively

Created on 18 Jun 2018  路  7Comments  路  Source: realm/realm-cocoa

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)
O-Community T-Help

Most helpful comment

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.

All 7 comments

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?

@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 馃憤

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.

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.

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.

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.

Closing due to age, if you have any more questions please open a new issue.

Thanks.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

TheHmmka picture TheHmmka  路  3Comments

dennisgec picture dennisgec  路  3Comments

ciminuv picture ciminuv  路  3Comments

javierjulio picture javierjulio  路  3Comments

thomasgravina picture thomasgravina  路  3Comments