Realm object has been deleted or invalidated

微笑、不失礼 提交于 2019-11-28 19:05:37
segiddins

You can check if an object has been deleted from the Realm by calling object.invalidated -- if it returns true, then it has been deleted or the Realm has manually invalidated.

wjiee weofej

I got a really nice way to catch a RLMException within Swift.

Currently Swift doesn't show where a RLMException happened.

In Realm/RLMUtil.mm:266, there is the definition for RLMException.

If you change the code to generate a swift error,

Xcode now can show you where the exception occurred.

Now it is a part of Swift.

// Realm/RLMUtil.mm:266
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
    // add some code to generate a swift error. E.g. division-by-zero.
    int a = 0;
    if (reason == nil) {
        a = 1;
    }
    NSLog(@"calculating 1 / %d = %f", a, 1 / a);

    ... remainder of the original code...
}
NazarYavornytskyy

I've just place breakpoint inside method:

// Realm/RLMUtil.mm:193
static NSException *RLMException(NSString *reason, NSDictionary *additionalUserInfo) {
    // ...
}

And on left panel you can check stack trace, here you can find where error throws.

The issue was in my FavoritesHelper class. It had both a favoriteLeagueIDs and favoriteLeagues property. I always set both of them and used the IDs for internal usage and the other property for whenever I want some data from these leagues.

This meant, that all favorite leagues were constantly referenced by the favoriteLeagues property (of the type [League]), thus crashing the app when I wanted to fetch them after they're invalidated.

What I've done to fix this, was to change the property favoriteLeagues to a computed property as follows:

var favoriteLeagues: [League] {
    get {
        var leagues = [League]()
        for id in favoriteLeagueIDs {
            if let league = realm.objectForPrimaryKey(League.self, key: id) {
                leagues.append(league)
            }
        }
        return leagues
    }
}

Now the leagues are no longer referenced and just get loaded from the database when I need to read them. Invalidated or deleted objects don't get loaded because of the if let statement (the Realm.objectForPrimaryKey(:key:) method returns nil in such a case).

you can calling isInvalidated or invalidated to judge the object is invalid (YES) or not (NO). you can also write a a custom method to define what is real invalidate

look for the document ,we will see a property:

 /**
 Indicates if the object can no longer be accessed because it is now invalid.

 An object can no longer be accessed if the object has been deleted from the Realm that manages it, or
 if `invalidate` is called on that Realm.
 */
@property (nonatomic, readonly, getter = isInvalidated) BOOL invalidated;

the invalidated is readonly but what does isInvalidated mean?

it is equals to - (BOOL)invalidated { return invalidated; }

it means that you can write a a custom method to define what is real invalidate you want.

Try to create the element on realm instead of add

So:

try! realm.write {
        realm.add(...)
    }

Replace with:

try! realm.write {
        realm.create(...)
    }

And then after the delete operation realm should work as expected!

I was unable to place breakpoints within the Realm framework itself as others suggested, but instead placed an exception breakpoint on my entire project:

This allowed me to catch a proper stack trace when the exception was thrown, and find my bug.

after spent a day, i figure out with the remove DispatchQueue.main.async in my realm.delete() function and finally it worked.

DispatchQueue.main.async {
  realm.delete()
}

to

realm.delete()

In my experience, if you trying to use target object (which you wanna delete) after delete, application will crash.

If you wanna trigger some code blocks after removing realm object, just trying to trigger that block right before the object removing in the memory. Trying to usage that object after removed from memory, will make some problem and will crash the app.

For example:

    try! realm.write {
        print("deleted word: \(targetObject.word)")
        realm.delete(targetObject)

        //  targetObject was removed, so don't try to access it otherwise you gonna got the 'nil' value instead of object.
    }

What you can do is observe the Results<> object returned from your initial Realm query (the one used to populate your list/table view) and update that list in the observer callback method whenever there is a change in the database.

Just make sure that you use the same Realm instance to delete/modify the object as the one used for the initial query.

EDIT:

Some code examples:

let realm = Realm()
let results = realm.objects(Favourite.self)
let notificationToken = results.observe { [weak self] (changes) in
    guard let tableView = self?.tableView else { return }
    tableView.reloadData()
}

// Somewhere else
try? realm.write {
    if let favourite = results.first {
         realm.delete(favourite)
    }
}

// OR
if let objectRealm = results.first?.realm {
    try? objectRealm.write {
        objectRealm.delete(results.first!)
    }
}

// Don't forget to stop observing in deinit{}
// notificationToken.invalidate()
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!