领域 - 删除后无法使用对象 [英] Realm - Can't use object after having been deleted
问题描述
我的应用中有一个视频播放器。集合视图中有一个视频列表。如果您点击其中一个单元格,则会出现一个新的视图控制器来播放所选的视频。此外,您可以在此新视图控制器中循环浏览集合视图中的所有视频,因为传递了整个列表。
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 twoList
, the original one saved in Realm and a copy of thisList
to power the UI. Let's call the listsfavoriteList
andcopyList
.
所以在 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 $ c $中删除对象c>然后在关闭控制器时从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 theVideo
class. When the user unfavorites the video, remove theVideo
object fromFavoriteList.videos
, set that property totrue
, 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 whereisDeleted
istrue
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 theVideo
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 doinglet unmanagedVideo = Video(value: video)
.
这篇关于领域 - 删除后无法使用对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!