为什么选择器被发送到以前的视图控制器?

时间:2017-04-03 16:09:21

标签: ios iphone swift

我有一个应用程序,其中包含一个声明协议的数据模型类,以及一个嵌入在导航控制器中的两个视图控制器。数据模型类是共享实例。两个视图控制器都是数据模型的委托。第二个视图控制器有一个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

我很乐意提供更多代码,但我怀疑知道的人可以从我提供的内容中进行诊断。

2 个答案:

答案 0 :(得分:0)

似乎正在发生的事情是:

  • PracticesViewController中,您将模型的委托设置为self,与pModel.delegate = self一样。
  • 您导航回第一个视图控制器。这意味着PracticesViewController被取消分配。但是模型的委托没有被更改,所以它仍然指向视图控制器曾经存在的内存位置。
  • 稍后(但很快),您的模型尝试在其委托上调用方法,但它不能因为它已被释放。这会导致您的应用崩溃。

您可以重新分配代理的值,例如在viewDidAppear中。或者您可以让模型在尝试调用之前检查其委托是否实现了方法。这是optional协议方法的标准做法 - 因为它们不必实现,所以在调用之前先检查它们。

一般情况下,不要在Swift中使用!,除非您希望代码在出现问题时崩溃。

答案 1 :(得分:0)

这里有一些问题。

  1. 您正在尝试重复使用委托,但只在viewDidLoad中进行设置。只有在顾名思义,视图最初加载时才会调用viewDidLoad。这意味着如果你在导航控制器中有VC1,那么你推送到VC​​2,然后回到VC1,viewDidLoad第二次调用。视图已加载。如果您想在每次视图控制器重新聚焦时调用一段代码,您应该将其放入viewWillAppear:viewDidAppear:

  2. 您正在展开一个您尚未实际实施的可选协议方法(由于第一个问题您将看到的错误,但它仍然是一个问题在其自己的)。将力展开更改为可选的self.delegate?.noDupe?(result:"none")

  3. 您还宣布了您的代理人var delegate: PracticesDataDelegate?。这使您的类保留其委托,通常不是正确的行为。在您的情况下,它实际上会导致保留周期(直到您更改委托)。您应将此声明更改为weak var delegate: PracticesDataDelegate?