4

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

问题是:当用户在 中时PlayerVC,他们可以取消收藏Video. 如果他们这样做,我会Video从 Realm 中删除该对象。但是,这会导致:

Terminating app due to uncaught exception 'RLMException', reason: 'Object has been deleted or invalidated.'

基本上,如果用户正在观看视频PlayerVC并且他不喜欢视频,我希望他们暂时仍然能够观看视频。但是当他们离开时应该更新PlayerVC集合视图。FavoritesVCVideo

当我删除一个Video对象时,我使用 Realm 的delete方法。

这是我保存Video对象列表的代码:

/// 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"
    }
}

这是我的Video类,它有一个isFavorite属性:

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...
}

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

/// 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.
    }
}

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

/// 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)
        }
    }
}

最终,我希望将不喜欢的Video对象从 Realm 中完全删除。只是不确定在这种情况下如何/何时执行此操作。

有什么想法吗?

更新 1

解决此问题的一种方法是:

  • 制作副本的非托管副本Video,并使用该副本为视图控制器的 UI 供电。

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

  • PlayerVC收到两个,一个保存在 Realm 中的原始文件和一个用于驱动 UIList的副本。List让我们称列表favoriteListcopyList.

  • 所以在里面didToggleStarButton我们会做这样的事情:

代码:

/// 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.
    }
}

有什么想法吗?

4

2 回答 2

1

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

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

您可能可以考虑几件事。

  • isDeleted属性添加到Video类。当用户取消收藏视频时,Video从 中删除对象FavoriteList.videos,将该属性设置为true,但将其保留在 Realm 中。稍后(当应用程序退出或视图控制器被关闭时),您可以对所有对象进行一般查询,isDeleted然后true删除它们(这解决了无头问题)。
  • 由于您的架构需要一个依赖于可以从其下删除的模型的视图控制器,这取决于您从该对象中使用了多少信息,因此制作副本的非托管副本并使用该副本Video可能更安全Video为视图控制器的 UI 供电。您可以通过执行来创建现有领域对象的新副本let unmanagedVideo = Video(value: video)
于 2017-03-29T21:11:02.330 回答
0

这是解决方法。检查它是否有效。

 class PlayerViewControllerr: UIViewController {

          var arrayForIndex = [Int]()
          var videos = List<Video>()


          @IBAction func didToggleStarButton(_ sender: UIButton) {
               self.arrayForIndex.append(currentIndexInVideosList)     
           }

         @overide func viewWillDisappear(_ animated : Bool){
              super.viewWillDisappear(animated)
              for i in arrayForIndex{
                 let realm = try! Realm()
        try! realm.write {
            let videoToDelete = videos[i] /// Get the video that is currently playing
            realm.delete(videoToDelete)
        }
       }
        }
于 2017-03-29T01:31:15.867 回答