领域 - 删除后无法使用对象 [英] Realm - Can't use object after having been deleted

查看:116
本文介绍了领域 - 删除后无法使用对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的应用中有一个视频播放器。集合视图中有一个视频列表。如果您点击其中一个单元格,则会出现一个新的视图控制器来播放所选的视频。此外,您可以在此新视图控制器中循环浏览集合视图中的所有视频,因为传递了整个列表。

I have a video player in my app. There is a list of videos in a collection view. If you tap on one of the cells, a new view controller appears to play the selected video. Also, you can cycle through all of the videos from the collection view in this new view controller because the entire list is passed.

问题是:
当用户位于 PlayerVC 时,他们可以不喜欢视频。如果他们这样做,我从Realm删除视频对象。但是,这会导致:

The problem is: When the user is in the PlayerVC, they can unfavorite a Video. If they do this, I delete the Video object from Realm. However, this causes a:

由于未捕获的异常'RLMException'而终止应用程序,原因:'对象已被删除或无效。'

基本上,如果用户正在观看 PlayerVC 中的视频他不喜欢视频,我希望他们暂时能够观看视频。 但是当他们离开 PlayerVC 时,应更新 FavoritesVC 中的集合视图,不再显示视频

Basically, if a user is watching a video in PlayerVC and he unfavorites a video, I want them to still be able to watch the video for the time being. But when they leave the PlayerVC, the collection view in FavoritesVC should be updated and not show that Video any more.

当我删除视频对象时,我使用Realm的 delete 方法。

When I delete a Video object I use Realm's delete method.

这是我的代码,用于保存视频对象列表:

This is my code to hold a list of Video objects:

/// Model class that manages the ordering of `Video` objects.
final class FavoriteList: Object {
    // MARK: - Properties

    /// `objectId` is set to a static value so that only
    /// one `FavoriteList` object could be saved into Realm.
    dynamic var objectId = 0

    let videos = List<Video>()

    // MARK: - Realm Meta Information

    override class func primaryKey() -> String? {
        return "objectId"
    }
}

这是我的视频类,其中 isFavorite 属性:

This is my Video class which has an isFavorite property:

final class Video: Object {
    // MARK: - Properties

    dynamic var title = ""
    dynamic var descriptionText = ""
    dynamic var date = ""
    dynamic var videoId = ""
    dynamic var category = ""
    dynamic var duration = 0
    dynamic var fullURL = ""
    dynamic var creatorSite = ""
    dynamic var creatorName = ""
    dynamic var creatorURL = ""

    // MARK: FileManager Properties (Files are stored on disk for `Video` object).

    /*
        These are file names (e.g., myFile.mp4, myFile.jpg)
    */
    dynamic var previewLocalFileName: String?
    dynamic var stitchedImageLocalFileName: String?
    dynamic var placeholderLocalFileName: String?

    /*
        These are partial paths (e.g., bundleID/Feed/myFile.mp4,     bundleID/Favorites/myFile.mp4)
        They are used to build the full path/URL at runtime.
    */
    dynamic var previewLocalFilePath: String?
    dynamic var stitchedImageLocalFilePath: String?
    dynamic var placeholderLocalFilePath: String?

    // Other code...
}

这个是我的代码在集合视图中显示视频对象(注意:我使用 RealmCollectionChange 来更新集合视图删除和插入单元格):

This is my code to show the Video objects in a collection view (Note: I use RealmCollectionChange to update the collection view for deleting and inserting cells):

/// This view controller has a `collectioView` to show the favorites.
class FavoriteCollectionViewController: UIViewController {
    // MARK: Properties

    let favoriteList: FavoriteList = {
        let realm = try! Realm()
        return realm.objects(FavoriteList.self).first!
    }()

    // Realm notification token to update collection view.
    var notificationToken: NotificationToken?

    // MARK: Collection View

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return favoriteList.videos.count
    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: FavoritesCollectionViewCell.reuseIdentifier, for: indexPath) as! FavoritesCollectionViewCell
        cell.video = favoriteList.videos[indexPath.item]

        return cell
    }

    // I pass this lst forward to the PlayerVC
    func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
        if let playerVC = self.storyboard?.instantiateViewController(withIdentifier: "PlayerViewController") as? PlayerViewController {
            // I pass the videos here.
            playerVC.videos = favoriteList.videos
            self.parent?.present(playerVC, animated: true, completion: nil)
        }
    }

    // MARK: Realm Notifications


    func updateUI(with changes: RealmCollectionChange<List<Video>>) {
        // This is code to update the collection view.
    }
}

最后,这是允许用户使用的代码在所有视频对象之间播放和循环:

Finally, this is the code to allow the user to play and cycle between all Video objects:

/// This view controller uses `AVFoundation` to play the videos from `FavoriteCollectionViewController`.
class PlayerViewControllerr: UIViewController {

    /// This `videos` is passed from `FavoriteCollectionViewController`
    var videos = List<Video>()

    // HELP: The app crashes here if I unfavorite a `Video`.
    @IBAction func didToggleStarButton(_ sender: UIButton) {
        let realm = try! Realm()
        try! realm.write {
            let videoToDelete = videos[currentIndexInVideosList] /// Get the video that is currently playing
            realm.delete(videoToDelete)
        }
    }
}

最终我想要的视频对象是无法从领域完全删除。在这种情况下,只是不确定如何/何时这样做。

Ultimately I want the Video object that is unfavorited to be completely deleted from Realm. Just not sure how/when to do it in this case.

有什么想法吗?

解决此问题的一个选项是:

An option to solve this is to:


  • 制作<的非托管副本code>视频复制,并使用它来为视图控制器的UI供电。

  • Make an unmanaged copy of the Video copy, and use that one to power the UI of the view controller.

我认为这可能有效的方式是:

The way I see this possibly working is this:


  • PlayerVC 将收到两个 List ,原始的一个保存在Realm中,另一个 List 的副本用于为UI供电。让我们调用列表 favoriteList copyList

  • The PlayerVC will receive two List, the original one saved in Realm and a copy of this List to power the UI. Let's call the lists favoriteList and copyList.

所以在 didToggleStarButton 里面,我们会这样做:

So inside of the didToggleStarButton we would do something like this:

代码:

/// This view controller uses `AVFoundation` to play the videos from `FavoriteCollectionViewController`.
class PlayerViewControllerr: UIViewController {

    /// A button to allow the user to favorite and unfavorite a `Video`
    @IBOutlet weak var starButton: UIButton!

    /// This is passed from `FavoriteCollectionViewController`
    var favoriteList: FavoriteList!

    /// A copy of the `FavoriteList` videos to power the UI.
    var copiedList: List<Video>!

    var currentIndexOfVideoInCopiedList: Int!

    override func viewDidLoad() {
        super viewDidLoad()

        // Make a copy of the favoriteList to power the UI.
        var copiedVideos = [Video]()

        for video in favoriteList.videos {
            let unmanagedVideo = Video(value: video)
            copiedVideos.append(unmanagedVideo)
        }

        self.copiedList.append(copiedVideos)
    }

    // HELP: The app crashes here if I unfavorite a `Video`.
    @IBAction func didToggleStarButton(_ sender: UIButton) {
        // Do the unfavoriting and favoriting here.


        // An example of unfavoriting:
        let realm = try! Realm()
        try! realm.write {
            let videoToDeleteFromFavoriteList = favoriteList.videos[currentIndexOfVideoInCopiedList] /// Get the video that is currently playing
            realm.delete(videoToDeleteFromOriginalList)
        }

        // Update star button to a new image depending on if the `Video` is favorited or not.
        starButton.isSelected = //... update based on if the `Video` in the `FavoriteList` or not.
    }
}

有什么想法吗?

推荐答案

由于许多架构原因,这个绝对是棘手的。

This one is definitely tricky for a number of architectural reasons.

你是对的,你可以只是从 FavoriteList.videos 然后在关闭控制器时从Realm中正确删除它,但你是对的,如果用户点击主页按钮,或者应用程序在此之前崩溃,你最终会得到一个无头视频对象。你需要能够确保你能够跟踪它。

You're right in that you could simply remove the object from the FavoriteList.videos and then properly delete it from Realm when going to dismiss the controller, but you're right that if the user clicks the home button, or the app crashes before then, you'll end up with a headless video object. You would need to be able to make sure you can track that.

你可以考虑一些事情。


  • isDeleted 属性添加到视频类。当用户不喜欢该视频时,从 FavoriteList.videos 中删除​​视频对象,将该属性设置为 true ,但保留在Realm中。稍后(当应用程序退出或视图控制器被解除时),您可以对 isDeleted true的所有对象执行常规查询/ code>然后删除它们(这解决了无头问题)。

  • 由于您的架构需要视图控制器依赖于可以从其下删除的模型,具体取决于您从视频对象中使用了多少信息,制作视频的非托管副本可能更安全复制,并使用它来驱动视图控制器的UI。您可以通过执行 let unmanagedVideo = Video(值:视频)来创建现有Realm对象的新副本。

  • Add an isDeleted property to the Video class. When the user unfavorites the video, remove the Video object from FavoriteList.videos, set that property to true, but leave it in Realm. Later on (either when the app quits or the view controller is dismissed), you can then do a general query for all objects where isDeleted is true and delete them then (This solves the headless problem).
  • Since your architecture requires a view controller relying on a model that could be deleted from under it, depending on how much information you're using from that Video object, it might be safer to make an unmanaged copy of the Video copy, and use that one to power the UI of the view controller. You can create a new copy of an existing Realm object by doing let unmanagedVideo = Video(value: video).

这篇关于领域 - 删除后无法使用对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆