

こんにちは

SwiftでiPhoneアプリを開発しています。

Segueを使ってUIKit用のViewControllerと  
SpriteKit用のGameViewControllerとを行き来できるようにしたのですが  
iPhoneシミュレーターで実行すると、デバッグナビゲーターのメモリが  
画面を切り替えるごとに30Mずつ増加して頭を抱えています。  
やりたいことは実現できたのに、悔しいです。  
もしよければお知恵をお貸しください。

環境  
OSX 10.9.5  
Xcode.6.1.1

  1. New -> Project -> Gameを選択
  2. New -> File -> Swift File -> FirstViewController.swiftを作成
  3. ライブラリよりViewControllerをストリーボードにドラッグし、Custom ClassでFirstViewControllerを設定
  4. 矢印をFirstViewControllerにする
  5. storyboardからFirstViewControllerを選択し、GameViewControllerにsegue接続。segueのidentifierにtoGameとつける
  6. storyboard上のGameViewControllerを選択し、自らのExitに接続。identifierにbackとつける

各ファイルの抜粋は以下の通りです

[code]

    class FirstViewController: UIViewController {
        @IBAction func returnMenu(segue: UIStoryboardSegue) {
            println("Welcome back!")
        }
    
        override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
            performSegueWithIdentifier("toGame", sender: nil)
        }
    }
    
    
    class GameViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
                scene.viewController = self
                skView.presentScene(scene)
            }
        }
    }
    
    class GameScene: SKScene {
    
        var viewController: UIViewController?
    
        override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
            for touch: AnyObject in touches {
            self.viewController?.performSegueWithIdentifier("back", sender: nil)
        }
    }
    
[/code]

どうぞ是非よろしくお願いします。


CC BY-SA 3.0
2015-04-09T12:31:36.707
8955
2015-10-08T01:07:56.733
9238
question
swift
UIKit<-segue->SpriteKit間の遷移でメモリリーク


メモリ「リーク」（使わなくなったメモリが解放されない現象）は、参照の循環が起きていることが原因で、クラスGameSceneのプロパティ`viewController`を弱参照（weak）にすることで、参照の循環を回避することができます。（コメント欄で指摘されているとおりです）

ここでは、それ以外の解決法として、タッチイベントに対するレスポンダチェーン（Respoder Chain）の活用を挙げたいと思います。

[code]

    class FirstViewController: UIViewController {
        @IBAction func returnMenu(segue: UIStoryboardSegue) {
            println("Welcome back!")
        }
    
        override func touchesEnded(touches: NSSet, withEvent event: UIEvent) {
            performSegueWithIdentifier("toGame", sender: nil)
        }
    }
    
    
    class GameViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
    
            if let scene = GameScene.unarchiveFromFile("GameScene") as? GameScene {
                // scene.viewController = self // 削除
                skView.presentScene(scene)
            }
        }
    
        // GameSceneクラスから移植。
        override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
            for touch: AnyObject in touches {
            self.performSegueWithIdentifier("back", sender: nil)
        }
    }
    
    class GameScene: SKScene {
    
        // var viewController: UIViewController? // 削除
    /* タッチイベントの削除。GameViewControllerに移植する。
        override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
            for touch: AnyObject in touches {
            self.viewController?.performSegueWithIdentifier("back", sender: nil)
        }
    */
    }
    
[/code]

タッチイベントのレスポンダチェーンとは、タッチイベントを受け取るレシーバ（メソッド）が見つかるまで、タッチイベントはUIResponderのサブクラスの間を、順に受け渡されていくという、メカニズムのことです。SKNode（SKScene）で、タッチイベント（touchBegan）が受け取られなければ、UIViewのサブクラス（SKView）に渡され、UIViewControllerのサブクラス（GameViewController）に渡され、そこでも受け取られなければ、UIWindowに渡されます。けっきょくUIApplicationまで行って、そこでも処理されなければ、深淵の闇に送られていきます。  
レスポンダチェーンを利用すれば、GameSceneクラスで、あえてタッチイベントを処理しないという方法で、GameViewControllerにタッチイベントを渡すことができるのです。


CC BY-SA 3.0
2015-04-10T06:22:36.493
8995
2015-04-10T06:22:36.493
7362
8955
answer