Reputation: 553
This is a Swift 2.1 project with an iOS 8.0+ target.
Much like user Cybermew in this question, I have a set-up where A->B->C->D. A is a navigation controller and the rest are regular UIViewControllers. When unwinding from D to B, C does not get released. This results in a memory leak and other unintended side-effects.
As opposed to the question linked above, I don't have any NSTimer that I know of that might keep the view in memory. What could be causing this to happen?
Code of C (trimmed):
import UIKit
import SpriteKit
import AVFoundation
import AVFoundation.AVAudioSession
class GameViewController: UIViewController {
var scene: GameScene!
var level: Level!
@IBOutlet weak var gameOverPanel: UIImageView!
@IBOutlet weak var heroPicture: UIImageView!
@IBOutlet weak var villainPicture: UIImageView!
@IBOutlet weak var spellOneButton: UIButton!
@IBOutlet weak var doubleLife: UISwitch!
let attackSound = SKAction.playSoundFileNamed("Attack.wav", waitForCompletion: false)
var tapGestureRecognizer: UITapGestureRecognizer!
var backgroundMusic: AVAudioPlayer = {
let sess = AVAudioSession.sharedInstance()
if sess.otherAudioPlaying {
_ = try? sess.setCategory(AVAudioSessionCategoryAmbient, withOptions: [])
_ = try? sess.setActive(true, withOptions: [])
}
let url = NSBundle.mainBundle().URLForResource("BackgroundMusic", withExtension: "mp3")
let player = try? AVAudioPlayer(contentsOfURL: url!)
player!.numberOfLoops = -1
return player!
}()
override func prefersStatusBarHidden() -> Bool {
return true
}
override func shouldAutorotate() -> Bool {
return true
}
override func supportedInterfaceOrientations() -> UIInterfaceOrientationMask {
return UIInterfaceOrientationMask.AllButUpsideDown
}
override func viewDidLoad() {
super.viewDidLoad()
let skView = view as! SKView
skView.multipleTouchEnabled = false
scene = GameScene(size: skView.bounds.size)
scene.scaleMode = .AspectFill
level = Level(filename: "Level_0")
scene.level = level
scene.addTiles()
scene.swipeHandler = handleSwipe
skView.presentScene(scene)
backgroundMusic.play()
beginGame()
}
}
Moved here from chat:
swipeHandler
is declared as:
var swipeHandler: ((Swap) -> ())?
and swap is:
struct Swap: CustomStringConvertible, Hashable {
// blabla
}
and once filled with a swap if calls this on the GameViewController
:
func handleSwipe(swap: Swap) {
view.userInteractionEnabled = false
if level.isPossibleSwap(swap) {
level.performSwap(swap)
scene.animateSwap(swap, completion: handleMatches)
} else {
scene.animateInvalidSwap(swap) {
self.view.userInteractionEnabled = true
}
}
Upvotes: 0
Views: 103
Reputation: 154721
You have a reference loop that is causing your viewController not to be deallocated. I would recommend turning swipeHandler
into a weak delegate pointer and you can call handleSwipe
though that pointer. That way, your GameScene will not retain any strong pointers to your GameViewController which will allow it to be freed when the unwind segue is performed.
protocol SwipeHandler: class {
func handleSwipe(swap: Swap)
}
class GameViewController: UIViewController, SwipeHandler {
var level = Level()
var scene: GameScene!
override func viewDidLoad() {
super.viewDidLoad()
scene = GameScene()
scene.swipeHandler = self
}
func handleSwipe(swap: Swap) {
// ...
}
}
class GameScene {
weak var swipeHandler: SwipeHandler?
// function to demonstrate call to handleSwipe
func useSwipeHandler() {
let swap = Swap()
swipeHandler?.handleSwipe(swap)
}
}
Upvotes: 1