【问题标题】:Can only delete an object from the Realm it belongs to Swift只能从它属于 Swift 的 Realm 中删除一个对象
【发布时间】:2021-03-29 21:56:36
【问题描述】:

我正在尝试在我的 NotesApp 中从领域中删除笔记并遇到此错误:“只能从它所属的领域中删除一个对象”。此笔记之前也保存在 Realm 和 可以通过点击我的 FSCalendar 中的日期在我的 TableView 中显示它。我试图用realm.create(item)替换realm.add(item),但也得到了错误:“无法将'T'类型的值转换为预期的参数类型'Object.Type'(又名'RealmSwiftObject.Type') ”。我是编程新手,所以任何帮助将不胜感激。以下是相关代码:

in my ToDoListItem.swift

class ToDoListItem: Object {
@objc dynamic var noteName: String = ""
@objc dynamic var date: Date =  Date()
@objc dynamic var descriptionText: String = ""
@objc dynamic var noteImage = Data()

init(date: Date, noteName: String) {
    self.date = date
    self.noteName = noteName
}

override init() {
    self.noteName = ""
    self.date = Date()
    self.descriptionText = ""
    self.noteImage = Data()
}

}

in my RealmManager.swift

class RealmManager {

static let shared = RealmManager()
private let realm = try! Realm()

func write<T: Object>(item: T) {
    realm.beginWrite()
    realm.add(item)
    try! realm.commitWrite()
}

func getObjects<T: Object>(type: T.Type) -> [T] {
    return realm.objects(T.self).map({ $0 })
}

func delete<T: Object>(item: T) {

    try! realm.write {
        realm.delete(item)
    }
}

}

in my ViewController where i can edit and delete the notes

@IBAction func didTapDelete() {
    
    let note = ToDoListItem()
    RealmManager.shared.delete(item: note)
    
    self.deletionHandler?()
    navigationController?.popToRootViewController(animated: true)
}

and finally in my TableViewController where the notes are displayed  (honestly i think the problem is hidden here but cannot find it...

@IBOutlet var tableViewPlanner: UITableView!
@IBOutlet var calendarView: FSCalendar!

private var data = [ToDoListItem]()

var datesOfEvents: [String] {
    return self.data.map { DateFormatters.stringFromDatestamp(datestamp: Int($0.date.timeIntervalSince1970)) }
}

var items: [ToDoListItem] = []

func getCount(for Date: String) -> Int {
    var count: [String : Int] = [:]
    for date in datesOfEvents {
        count[date] = (count[date] ?? 0) + 1
    }
    return count[Date] ?? 0
}

func getEventsForDate(date: Date) -> [ToDoListItem] {
    let string = DateFormatters.stringFromDatestamp(datestamp: Int(date.timeIntervalSince1970))
    return self.data.filter {DateFormatters.stringFromDatestamp(datestamp: Int($0.date.timeIntervalSince1970)) == string }.sorted(by: {$0.date < $1.date})
}

override func viewDidLoad() {
    super.viewDidLoad()
    
    calendarView.rounding()
    tableViewPlanner.rounding()
    
    data = RealmManager.shared.getObjects(type: ToDoListItem.self)
    self.items = self.getEventsForDate(date: Date())
    
    calendarView.delegate = self
    calendarView.dataSource = self
    tableViewPlanner.delegate = self
    tableViewPlanner.dataSource = self
    
}

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(true)
    
    self.calendarView.select(Date())
    self.calendarView.reloadData()
    refresh()
}

//MARK:- TableView Data Source
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.items.count //data.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: K.plannerCellIdentifier, for: indexPath) as! NoteTableViewCell
    let note = self.items[indexPath.row]
    cell.configureCell(note: note)
    
    return cell
}

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    tableView.deselectRow(at: indexPath, animated: true)
    
    let note = data[indexPath.row]
    guard
        let vc = storyboard?.instantiateViewController(identifier: K.infoVCIdentifier) as? InfoViewController else { return }
    vc.note = note
    vc.deletionHandler = { [weak self] in
        self?.refresh()
    }
    vc.title = note.noteName
    navigationController?.pushViewController(vc, animated: true)
}

//MARK:- User Interaction
@IBAction func didTapAddButton() {
    guard
        let vc = storyboard?.instantiateViewController(identifier: K.entryVCIdentifier) as? EntryViewController else { return }
    vc.completionHandler = { [weak self] in
        self?.refresh()
    }
    vc.title = K.entryVCTitle
    navigationController?.pushViewController(vc, animated: true)
}

func refresh() {
    
    DispatchQueue.main.async {
        self.data = RealmManager.shared.getObjects(type: ToDoListItem.self)
        self.tableViewPlanner.reloadData()
        self.calendarView.reloadData()
    }
}

}

扩展PlannerViewController:FSCalendarDelegateAppearance & FSCalendarDataSource {

func calendar(_ calendar: FSCalendar, appearance: FSCalendarAppearance, eventDefaultColorsFor date: Date) -> [UIColor]? {
    let dateString = DateFormatters.yearAndMonthAndDateFormatter.string(from: date)
    if self.datesOfEvents.contains(dateString) {
        return [UIColor.blue]
    }
    return [UIColor.white]
}

func calendar(_ calendar: FSCalendar, didSelect date: Date, at monthPosition: FSCalendarMonthPosition) {
    self.items = self.getEventsForDate(date: date)
    self.tableViewPlanner.reloadData()

}

func calendar(_ calendar: FSCalendar, numberOfEventsFor date: Date) -> Int {
    let dateString = DateFormatters.yearAndMonthAndDateFormatter.string(from: date)
    let count = self.getCount(for: dateString)
    self.tableViewPlanner.reloadData()
    return count
}

}

【问题讨论】:

  • 您正在尝试删除 Realm 中不存在的对象。查看您的 tapToDelete 函数。您首先在内存中创建一个新项目let note = ToDoListItem(),然后尝试将其从领域RealmManager.shared.delete(item: note) 中删除。您必须将其写入 Realm 才能将其从 Realm 中删除。
  • 此外,此代码realm.objects(T.self).map({ $0 }) 将成为问题,因为您将实时更新领域对象转换为数组,然后将它们与领域断开连接,因此它们不再实时更新。此外,Realm Results 会延迟加载到数千个对象,对内存的影响很小。当将其转换为数组时,所有项目都加载到内存中,这可能会使设备不堪重负并导致其崩溃。见this answer
  • @Jay 非常感谢!那么,我究竟需要如何更改我的 tapToDelete() 方法?我的“let note”没用,我如何到达 Realm 中的真实对象?从我的 TableViewController 中,我将注释(通过单击它)发送到我的 InfoViewController,我可以在其中编辑或删除此注释(此处:github.com/NikolaiBorisov/DailyPlanner/blob/main/DailyPlanner/…)。以及如何更改此代码“realm.objects(T.self).map({ $0 })”以避免这些问题?抱歉这些问题,我只是想逐步了解我需要做什么。谢谢=)
  • 在高层次上,您的 tableView 由数据源支持 - 通常是数组或领域集合(结果或列表)。在该数组或列表中,您将拥有包含 tableView 中显示的任何数据的对象。如果点击删除表中的第 1 行,则需要获取行索引,在本例中为 1,从 dataSource 中获取该对象,然后将其从 Realm 中删除,并将其从 dataSource 中删除。对此有一些警告;如果您的 dataSource 是 Results 对象,它将自动更新。如果您观察对象,则通过观察事件更新列表。
  • @Jay 非常感谢!

标签: ios swift realm


【解决方案1】:

真正的问题是 didTapDelete 函数——你为什么要创建一个新注释只是为了删除它(我希望它只是为了测试领域删除语法)。您应该删除传递给视图控制器以进行编辑/删除的注释对象。 (确实选择行中的 vc.note -> 另一个 VC 中的 self.note - didTapDelete 所在的位置) 所以你的点按删除看起来像--

RealmManager.shared.delete(item: note)
//show deleted alert & go back

对错误的一点解释 - 仅实例化 Realm 对象 (ToDoListItem()) 不会将其添加到 Realm(数据库系统)。要删除/编辑领域对象,必须从领域中获取 (RealmManager.shared.getObjects(type: ToDoListItem.self)) 或添加到领域。

我建议在开始编写代码之前先阅读 Realm 教程(有很多)

【讨论】:

  • 谢谢你,我的朋友!我已经改变了我的 didTapDelete 函数:didTapDelete() { RealmManager.shared.delete(item: note!) } 但之后我得到了新的错误:“线程 1:“对象已被删除或无效。”“。在 TableViewController 中,我正在使用“private var data = [ToDoListItem]()”,而在 ViewController 中,我正在尝试删除注释,我正在使用“public var note:ToDoListItem?”也许是问题所在?这也是完整的项目:github.com/NikolaiBorisov/DailyPlanner,也许它有助于澄清我的问题。提前感谢您!=)
  • 我查看了该项目 - 无法弄清楚您为什么会收到该错误。我会推荐一些事情 - 1. 用单个对象替换数据和项目 - Results 类型的项目,您必须对 RealmManager 中的获取对象方法执行相同操作。 2.代替刷新方法和删除,完成块,在上述项目上使用领域观察者块(Results)。那应该解决问题。我最好的猜测是因为复制了数据和项目中的项目(尽管它仍然不应该导致崩溃)。无论如何,以上几点至少会改进代码。
  • 非常感谢,我的朋友!我会努力的!
  • @NikolaiBorisov 没有理由写一个对象来删除它。您正在尝试从 Realm 中删除 Realm 中不存在的对象。这个let note = ToDoListItem() 创建了一个新对象。如果您想删除tapToDelete 中的现有对象,那不是这样做的。哦,看看我的评论,因为您还有其他问题需要解决。
猜你喜欢
  • 1970-01-01
  • 2017-08-12
  • 2018-07-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-12
相关资源
最近更新 更多