我有一个应用程序,其中包含一个声明协议的数据模型类,以及一个嵌入在导航控制器中的两个视图控制器。数据模型类是共享实例。两个视图控制器都是数据模型的委托。第二个视图控制器有一个UITableView。
一开始,从第一个视图控制器调用数据模型函数按预期工作。当我从第一个视图控制器转到第二个视图控制器时,对数据模型函数的调用也按预期工作。
但是,当我导航回第一个视图控制器并调用数据模型函数时,应用程序崩溃并出现此错误:
2017-04-03 09:48:12.623027 CoreDataTest [3207:1368182] - [CoreDataTest.PracticesViewController noDupeWithResult:]:无法识别的选择器发送到实例0x15fe136e0
该PracticesViewController是第二个视图控制器。我不明白为什么选择器被发送到我正在考虑的前一个视图控制器。我的期望是选择器应该被发送到刚导航回的第一个视图控制器,
我是自学成才的,所以我认为我缺少一些基本的东西,但我不知道自己不知道什么。有人可以解释崩溃发生的原因吗?
数据模型的代码
import Foundation
import CoreData
import UIKit
@objc protocol PracticesDataDelegate {
@objc optional func practicesLoadError(headline:String,message:String)
@objc optional func practicesLoaded(practices:[NSManagedObject])
@objc optional func practiceStored()
@objc optional func practiceDeleted()
@objc optional func noDupe(result:String)
}
class PracticesDataModel {
static let sharedInstance = PracticesDataModel()
private init () {}
var delegate: PracticesDataDelegate?
var practices: [NSManagedObject] = []
// some code omitted . . .
/// Check for a duplicate exercise
func checkForDupe(title:String,ngroup:String,bowing:String,key:String){
guard let appDelegate =
UIApplication.shared.delegate as? AppDelegate else {
return
}
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Practice")
let predicate = NSPredicate(format: "exTitle == %@ AND notegroup == %@ AND bowing == %@ AND key == %@", title, ngroup, bowing, key)
fetchRequest.predicate = predicate
do {
practices = try managedContext.fetch(fetchRequest)
if practices.count == 0 {
self.delegate?.noDupe!(result:"none") // exception happens here
} else {
self.delegate?.noDupe!(result:"dupe")
}
} catch let error as NSError {
// to come
}
}
第一个视图控制器的顶部
import UIKit
class galamianSelection: UIViewController, ExcerciseDataModelDelegate, PracticesDataDelegate {
let exerciseModel = ExerciseDataModel.sharedInstance
let pModel = PracticesDataModel.sharedInstance
// some code omitted . . .
//// THE VIEW ////
override func viewDidLoad() {
super.viewDidLoad()
exerciseModel.delegate = self
pModel.delegate = self
exerciseModel.loadExercises()
}
//// RESPOND TO PRACTICE DATA MODEL ////
func practiceStored() {
print("exercise stored")
}
func noDupe(result: String) {
if result == "none" {
let d = Date()
pModel.storePractice(date: d, exTitle: theExerciseTitle.text!, notegroup: theNoteGroup.text!, bowing: theBowings.text!, rythem: theRhythms.text!, key: theKey.text!, notes: "")
} else {
print("dupe")
}
}
第二个视图控制器的顶部
import UIKit
class PracticesViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, PracticesDataDelegate {
@IBOutlet weak var tableView: UITableView!
let pModel = PracticesDataModel.sharedInstance
// some code omitted . . .
//// THE VIEW ////
override func viewDidLoad() {
super.viewDidLoad()
pModel.delegate = self
pModel.getPractices()
}
在两个视图控制器中,委托都已正确设置为self
。
我很乐意提供更多代码,但我怀疑知道的人可以从我提供的内容中进行诊断。
答案 0 :(得分:0)
似乎正在发生的事情是:
PracticesViewController
中,您将模型的委托设置为self
,与pModel.delegate = self
一样。PracticesViewController
被取消分配。但是模型的委托没有被更改,所以它仍然指向视图控制器曾经存在的内存位置。您可以重新分配代理的值,例如在viewDidAppear
中。或者您可以让模型在尝试调用之前检查其委托是否实现了方法。这是optional
协议方法的标准做法 - 因为它们不必实现,所以在调用之前先检查它们。
一般情况下,不要在Swift中使用!
,除非您希望代码在出现问题时崩溃。
答案 1 :(得分:0)
这里有一些问题。
您正在尝试重复使用委托,但只在viewDidLoad
中进行设置。只有在顾名思义,视图最初加载时才会调用viewDidLoad
。这意味着如果你在导航控制器中有VC1,那么你推送到VC2,然后回到VC1,viewDidLoad
将不第二次调用。视图已加载。如果您想在每次视图控制器重新聚焦时调用一段代码,您应该将其放入viewWillAppear:
或viewDidAppear:
您正在展开一个您尚未实际实施的可选协议方法(由于第一个问题您将看到的错误,但它仍然是一个问题在其自己的)。将力展开更改为可选的self.delegate?.noDupe?(result:"none")
您还宣布了您的代理人var delegate: PracticesDataDelegate?
。这使您的类保留其委托,通常不是正确的行为。在您的情况下,它实际上会导致保留周期(直到您更改委托)。您应将此声明更改为weak var delegate: PracticesDataDelegate?