领域 – 删除后不能使用对象

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

问题是:当用户在PlayerVC ,他们可能不喜欢Video 。 如果他们这样做,我从Realm中删除Video对象。 但是,这会导致:

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

基本上,如果用户在PlayerVC观看video并且他不喜欢video,我希望他们暂时能够观看video。 但是当他们离开PlayerVCFavoritesVC的集合视图应该被更新并且不再显示该Video

当我删除一个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 (eg, myFile.mp4, myFile.jpg) */ dynamic var previewLocalFileName: String? dynamic var stitchedImageLocalFileName: String? dynamic var placeholderLocalFileName: String? /* These are partial paths (eg, 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) } } } 

最终,我想要从Realm中彻底删除不合适的Video对象。 只是不知道如何/何时在这种情况下。

有什么想法吗?

更新1

解决这个问题的一个select是:

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

我看到这可能工作的方式是这样的:

  • PlayerVC将接收两个List ,一个保存在Realm中的原始List ,以及这个List一个副本来为UI提供动力。 我们调用列表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. } } 

有什么想法吗?

对于一些体系结构的原因来说,这绝对是棘手的。

你是对的,你可以简单地从FavoriteList.videos移除对象,然后在closures控制器时从Realm中正确删除它,但是你是对的,如果用户点击homebutton,或者应用程序崩溃那么,你最终会得到一个无头的video对象。 你需要能够确保你可以跟踪。

有几件事情你可以考虑。

  • 将一个isDeleted属性添加到Video类。 当用户不满意video时,从FavoriteList.videos移除Video对象,将该属性设置为true ,但将其留在Realm中。 稍后(当应用程序退出或视图控制器被解除时),您可以对所有isDeletedtrue对象执行一般查询,然后将其删除(这解决了无头问题)。
  • 由于您的体系结构要求视图控制器依赖于可以从其下删除的模型,具体取决于您从Video对象中使用了多less信息,因此制作Video副本的非托pipe副本可能更安全,并使用一个为视图控制器的UI供电。 您可以通过let unmanagedVideo = Video(value: video)创build现有Realm对象的新副本。

这是解决方法。 检查它是否工作。

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