I get this error after I have deleted an object and try to create another object in its place:
"Realm object has been deleted or invalidated"
I am using the following code to create/recreate the object:
try! realm.write {
realm.create(ChatMessageRealm.self, value: realmChatMessage, update: true)
}
And I am deleteing the object with this code:
try! realm.write {
try! realm.delete(message)
}
This is how it is explained in the docs but I am still getting the error. Does anyone have an idea how to solve this?
I am getting this often when doing the exact same thing in Objective-c (Deleting an object and creating another one in it's place). I'm doing this on a background thread and both the delete operation and create operation runs successful but when I call out to the main thread to query for a fresh set of results (and after calling realm refresh), some of the results that the query returns are apparently "deleted or invalidated". I've been trying to work around this for the past month and getting no where so it feels like a bug.
Just curious, when you add/delete an object form realm do you instantiate Realm every time or do you define a Realm object once and use a reference to that Realm?
For example, would you use:
Realm().objects(AnyObject)
Realm().objects(SomeOtherObject)
or:
var realm: Realm?;
realm = try! Realm();
realm.objects(AnyObject);
realm.objects(SomeOtherObject);
The process flow goes like this:
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
...
[realm deleteObject:object]
...
[realm addObject:newObject]
[realm commitWriteTransaction]
//Notify main thread to query for a fresh result set
//Fresh result set ends up containing objects that are "deleted or invalidated"
+1
I'm not sure what the confusion here is. This "invalid object" exception will be thrown if you delete a Realm object, and then subsequently try to access it. Realm throws an exception when this happens because we consider accessing deleted objects to be a breach of our API contract.
Is there more to this than I'm not understanding? I'm happy to work with you to clarify this, and if you have any specific recommendations regarding how we could improve our docs around this, I'll be happy to consider them!
@jpsim there is no confusion on that. It is clear that accessing a deleted object should result in an exception. The problem is that after objects are deleted, they shouldn't be showing up in result sets when querying realm right after objects are deleted, which is exactly what is happening here. Does this make more sense?
Exactly what is happening for me. I retrieve a set of realm objects, put them into an array, display info from them in a TableViewController. Then I clear out the array, delete the objects from Realm, and next time I try to add an object in the array and access it it throws me that error. It is as if the objects are not being delete properly
Exactly same thing I am doing. Though I'm having a hard time reproducing it, it's happening a lot in crash reports and really hurting the app love.
Ah, thanks for clarifying. From my understanding of your code's description, the reason why an RLMResults contains objects which have been invalidated might have something to do with accessing elements of the RLMResults at different points in time when the Realm is at different transaction versions.
For example, doing something like the following would trigger the exception you're seeing:
// Background Thread
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm deleteObject:object];
[realm addObject:newObject];
[realm commitWriteTransaction];
// Notify main thread to query for a fresh result set
// Main Thread
MyModel *firstObject = [[MyModel objectsInRealm:realm] firstObject];
[realm refresh]; // => advances Realm to latest state, maybe different than when 'firstObject' was obtained
firstObject; // => invalid or deleted object exception
Keep in mind that your code may look different while still fitting this scenario.
If this is what's happening to you, the problem is likely that even once write transactions are committed, those changes may not be available to other threads instantly. This is explained in the Threading section of our docs.
The fact that both of you have trouble reproducing this reliably would lead credence to the theory that this is a race condition in your code.
One of the reasons why we recommend using Realm's built-in notification mechanism (see Notifications) over dispatching your own notifications is to avoid race conditions like these. If you must use your own notification mechanism, it's important to call -[RLMRealm refresh] before any access that could render an object accessor's state invalid like a deletion.
I'm not ruling out the possibility of a bug on our side, everything you've shared so far would seem to indicate that this is a race condition as I described. If you can rule out that possibility, I'd love to see a sample project that can reproduce the issue so we can make progress on resolving that.
Oh, also I'd like to emphasize that because Realm's queries are always live, and auto-advancing, it's unnecessary and even expensive to explicitly re-create an RLMResults as you're alluding to in your comment (// Notify main thread to query for a fresh result set). This is explained in the Auto-Updating Results section of our docs.
@jpsim I figured it was some kind of race condition. The problem is calling RLMRealm refresh is the first thing I do after notifying the main thread to get a fresh result set (because if I don't, getting outdated data is almost a sure thing every time). I've been working through this for over a month now and I can't seem to defend against this problem.
When I had integrated realm, the only way to get notified by realm was to get notified after ANY write transactions happened. I needed to specify which kind of writes had just finished, which is why I had to do my own notifications.
Is it possible that this problem will get fixed by using realms notification system? The main problem here is I can't even reproduce this error myself. I only see them in the crash reports.
When I had integrated realm, the only way to get notified by realm was to get notified after ANY write transactions happened. I needed to specify which kind of writes had just finished, which is why I had to do my own notifications.
100% understandable and what I would do when faced with the same problem.
Is it possible that this problem will get fixed by using realms notification system?
Not directly, but it would likely avoid the problem altogether, although it's hard to say without knowing what's happening here.
The main problem here is I can't even reproduce this error myself. I only see them in the crash reports.
I hope you understand it's difficult to rule out that this is a race condition in user code like I've described without a repro case. If you can't share your code here publicly, you can send it privately to [email protected] referencing this GitHub issue and we can look into it further with you.
@uknowmeright it would be useful if you also checked against your codebase for the potential pitfalls I mentioned above since your situation may be different than @edalford11's.
@uknowmeright have you had the chance to review my suggestions and see if my analysis applies to your situation? I'd love to help you in this matter.
Closing. Will reopen if we hear back from @uknowmeright
Wanted to circle back on this. Using the realm notification system removed the bug I was having entirely as the results passed from the realm notification block always had valid objects. That being said, it is definitely clear that after deleting objects from realm and calling
[realm commitWriteTransaction]
there is a small window right after the commit where if you query for objects that would have shown up in the result set had you not deleted those previous objects, you might get objects in the result set that are invalidated or deleted. It is not directly reproducible as I could never reproduce it myself but it was definitely showing up in the crash reports a pretty significant amount.
Hey sorry for the late response I have been very busy lately.
I solved this by Just incrementing through the Results object that Realm returns directly and putting it into the table instead of throwing the data into a swift array and then handling it. This made most of my invalidation errors go away. I also was using a global variable to reference a realm object. This ended up causing a lot of errors so Instead I just saved the ID of the object and grab it from Realm every time I need to read or update it.
After these two changes I haven't run into any errors. I hope this helps someone out there!
Even We had a similar issue....I followed the way as described in realm docs of using tableview with realm results.... Works perfectly....
Awesome !!!
@MangeshKarekar Can you please provide link for it? or you can explain the method here..
@TEJ227 here's our table view example: https://github.com/realm/realm-cocoa/blob/master/examples/ios/swift-2.2/TableView/TableViewController.swift
Thank You @jpsim !
i was experiencing some weird behavior, but not sure if i can point it to this
can the race condition problem happen also with this approach?
Collecting the object IDs and then fetching them on main thread
// Background Thread
RLMRealm *realm = [RLMRealm defaultRealm];
[realm beginWriteTransaction];
[realm deleteObject:object];
[realm addObject:newObject];
//collect object ID
[realm commitWriteTransaction];
// Execute block on main thread
//{
// fetch object by object ID
//}
@jpsim will this realm.deleteAll() helps stop all realm notification ? because it might caused a problem if the listener still running and try to access object
@atom2ueki if you have object notifications, you can react to .delete notifications.
I suggest you guys to check Realm official documentation. I was having a similar situation but I was getting error in background thread. The reason was that I wasn't calling refresh.
https://realm.io/docs/swift/latest/
When you initially open a Realm on a thread, its state will be based off the most recent successful write commit, and it will remain on that version until refreshed. Realms are automatically refreshed at the start of every runloop iteration, unless Realm鈥檚 autorefresh property is set to NO. If a thread has no runloop (which is generally the case in a background thread), then Realm.refresh() must be called manually in order to advance the transaction to the most recent state.
I have issue like this, my code was like this
let realm = try! Realm()
let cartItems = realm.objects(CartRealm.self).filter(queryString)
**
try! realm.write {
realm.delete(cartItems)
}
and when i changed the line between asterisk that writing on db into like this
realm.beginWrite()
realm.delete(cartItems)
try! realm.commitWrite()
the crash was gone.
I get the same error for similar condition.
@rjalkuino - I doubt it. More like fortuitous timing stopped you being able to detect the race condition which will still be occurring in the wild.
@discardableResult
public func write<Result>(withoutNotifying tokens: [NotificationToken] = [], _ block: (() throws -> Result)) throws -> Result {
beginWrite()
var ret: Result!
do {
ret = try block()
} catch let error {
if isInWriteTransaction { cancelWrite() }
throw error
}
if isInWriteTransaction { try commitWrite(withoutNotifying: tokens) }
return ret
}
Most helpful comment
@jpsim there is no confusion on that. It is clear that accessing a deleted object should result in an exception. The problem is that after objects are deleted, they shouldn't be showing up in result sets when querying realm right after objects are deleted, which is exactly what is happening here. Does this make more sense?