“致命错误:在展开可选值时意外发现 nil”,同时调用协议方法

Posted

技术标签:

【中文标题】“致命错误:在展开可选值时意外发现 nil”,同时调用协议方法【英文标题】:"fatal error: unexpectedly found nil while unwrapping an Optional value" while calling a protocol method 【发布时间】:2015-07-06 19:20:39 【问题描述】:

我已经实现了一个协议来获取某种消息,即游戏已经完成并将时间传递给我的控制器(游戏在 SpriteKit 中编写)。但是现在我在完成游戏并执行 gameEnded 方法后遇到了这个致命错误,并且应用程序崩溃了。有谁知道为什么?

这是我的代码

游戏视图控制器

class AcceleratorGameController: UIViewController 

var time: Float = 0.0
var challengeController: ChallengeViewController!
weak var delegate : GameEnded?

override func viewDidLoad() 
    super.viewDidLoad()
    if let scene = AcceleratorGame.unarchiveFromFile("AcceleratorGame") as? AcceleratorGame 

        let skView = self.view as! SKView
        skView.showsFPS = true
        scene.viewController = self

        skView.ignoresSiblingOrder = true

        scene.scaleMode = .AspectFill
        skView.presentScene(scene)
    



override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
    var dest : ChallengeViewController = segue.destinationViewController as! ChallengeViewController
    dest.gameHasFinished(time) //Dont know if this does anything...
    println("Segue now!")


override func viewWillDisappear(animated: Bool) 
    super.viewWillDisappear(true)
    println("View will disapear")

    delegate.gameHasFinished(self.time) // ###CRASHES HERE!###

这是我的另一个 ViewController

protocol GameEnded : class 
     func gameHasFinished(time: Float)


class ChallengeViewController : UIViewController, GameEnded 

var time : Float = 0.0
var acceleratorGameController : AcceleratorGameController!

override func viewDidLoad() 
    super.viewDidLoad()
    println("ChallengeViewController geladen")
    startGame()

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
    if segue.identifier == "accelGame" 
        acceleratorGameController = segue.destinationViewController as! AcceleratorGameController
        acceleratorGameController.delegate = self
    

func startGame () 
    println("Start Game")

    self.showAccelGameView()

func gameHasFinished(time: Float) 
    println("Game has finished")
    self.time = time

func showAccelGameView() 

    println("Show Accel Game")
    let storyboard = UIStoryboard(name: "Main", bundle: nil)
    let vc = storyboard.instantiateViewControllerWithIdentifier("AcceleratorGameController") as! AcceleratorGameController

    self.navigationController?.pushViewController(vc, animated: true)

编辑: 我做了你告诉我的所有事情,但它仍然在同一点崩溃。 Xcode 告诉我使用委托。我什么时候委托?它有效,但这不是我想要的。必须调用该函数!

func gameOver(time: Float) 
    println("Game Over")
    self.time = time
    //delegate!.gameHasFinished(time) still crashes here. added !. i commented it to test the prepareForSegue method

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
    var dest : ChallengeViewController = segue.destinationViewController as! ChallengeViewController
    dest.gameHasFinished(time)
    println("Segue now!") // Wont happen
    self.navigationController?.popViewControllerAnimated(true) //Wont happen either. If i call this in gameOver() it works.

我在prepareForSegue() 方法中添加了一些println(),但不知何故它们不会被执行。我必须以某种方式给他们打电话吗?这是我的segue的截图。

http://i.stack.imgur.com/oK4Ce.png

我也尝试过反之亦然,但也无济于事。

http://i.stack.imgur.com/TVmdP.png(没有标识符,因为它是唯一的转场。)

我忘了说这个AcceleratorGameController 背后的SpriteKit 游戏在完成后会调用gameOver() 方法。

【问题讨论】:

【参考方案1】:

首先,一些提示:

始终尝试通过以下方式声明您的协议(当参考 delegate 模式时):

protocol GameEnded : class 
    func gameHasFinished(time: Float)

然后,您可以通过上述方式,声明您的 delegate 变量 weak 并避免强引用。

你的weak var类应该是这样的:

class AcceleratorGameController: UIViewController 

   weak var delegate : GameEnded?

   // rest of your code

最后,您应该将引用保留为AcceleratorGameController 类的全局变量,以在prepareForSegue 中设置为您的delegate,如下所示:

class ChallengeViewController : UIViewController, GameEnded 

    var acceleratorGameController : AcceleratorGameController! 

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) 
         if segue.identifier == "accelGane" 
             acceleratorGameController = segue.destinationViewController as! AcceleratorGameController
             acceleratorGameController.delegate = self
         
    

上面的代码应该可以解决您的问题。希望对您有所帮助。

【讨论】:

我们为什么要担心弱/强引用?我认为 ARC 应该消除我们对此的担忧。 是的,不,它可能存在保留循环,您可以在***.com/questions/27327545/…@matt 的答案中阅读更多相关信息。 是的,这样更清楚。在采用委托模式时,我必须记住这一点。 我用你的提示编辑了这个问题,但它仍然不起作用【参考方案2】:

我不知道ChallengeViewController 将如何初始化AccelerometerGameController 的委托。

在您的prepareForSegue 中,只有当segue 标识符为accelGane 时,您才为GameController 设置委托。但是在showAccelGameView 中触发segue 时,您没有使用此标识符。如果您已经在情节提要中设置了 segue,请使用它来触发 segue

self.performSegueWithIdentifier("accelGane", sender: self)

【讨论】:

非常感谢对我的帮助! :)

以上是关于“致命错误:在展开可选值时意外发现 nil”,同时调用协议方法的主要内容,如果未能解决你的问题,请参考以下文章

Swift 错误致命错误:在展开可选值时意外发现 nil

@IBInspectable 致命错误:在展开可选值时意外发现 nil

为啥我收到错误:致命错误:在展开可选值时意外发现 nil?

Swift:致命错误:在展开可选值时意外发现 nil

swift 2 致命错误:在展开可选值时意外发现 nil - 类别名称

崩溃:致命错误:在展开可选值时意外发现 nil