Swift SpriteKit ARC for dummies - swift

I have been trying to wrap my head around strong reference cycles and I am struggling. I have been reading the documentation from apple and on some website and I feel like they dont really address my issues.
I understand you have to use weak and unowned depending if the object can be nil or not. So say you have to 2 classes like this
class Person {
var dog: Dog?
....
}
class Dog {
weak var person: Person?
}
that reference each other I know that one of them has to use weak/unowned. This is the classic example seen in most tutorials.
I also have seen examples like this and they too make sense to me
class Person {
unowned let gameScene: GameScene
init(scene: GameScene) {
self.gameScene = scene
....
}
I also understand that NSTimers can cause strong reference cycles if not invalidated. I am not using NSTimers so this should not be a problem.
I furthermore understand that protocols can cause memory leaks too, so they way to handle them would be to make it a class protocol
protocol TestDelegate: class { }
and where I am trying to reference the protocol make it a weak property
class: SomeClass {
weak var myDelegate: TestDelegate?
}
And finally I know about closures where I am capturing self like this
SKAction.runBlock { [unowned self] in
self.player.runAction....
}
However when it comes to my actual spritekit game I seem to have reference cycles all over the place, even for simple classes I am sure dont reference another class, and I dont understand why.
So my main questions are
1) Do all properties create strong reference cycles or only global properties that are created before a class is initialised?
2) What other things could potentially create a strong reference cycle in my simple classes?
For example I am using a class that creates platforms
class Platform: SKSpriteNode {
/// basic code to create platforms
/// some simple methods to rotate platforms, move them left or right with SKActions.
Now I have similar classes like this for Traps, Enemies etc. Again they normally just set the sprite properties (physics body etc) and have some methods to animate them or rotate them. There is nothing fancy going on in them, especially in regards to referencing other classes or scenes.
In my gameScene I have a method to create platforms (would be same for enemies, traps etc)
func createPlatform() {
let platform1 = Platform(.....
platformNode.addChild(platform1)
let platform2 = Platform(....
platformNode.addChild(platform2)
let platform3 = Platform(...
platformNode.addChild(platform3)
// platform node is just a SKNode in the gameScene to help maintain the difference zPositions of my objects.
}
When I am running allocations I can see that only 1 of the 3 platforms goes into transient state, 2 stay persistent when I change to my menuScene. Whats strange is that always only 1 gets removed, doesnt matter if I change the order or create/delete some platforms. So it seems they are creating strong references, except 1. So if I replay my level a few times I can quickly have 50-100 persistant platforms in memory. My scene therefore also doesn't get deinit which is more memory wasted.
Another example I have is
class Flag {
let post: SKSpriteNode
let flag: SKSpriteNode
init(postImage: String, flagImage: String) {
post = SKSpriteNode(imageNamed: postImage)
...
flag = SKSpriteNode(imageNamed: flagImage)
...
post.addChild(flag)
}
}
and same problem. I create some flags in my scenes and sometimes a flag doesnt get removed, sometimes it does. Again this class does not reference any scene or custom class, it merely creates a sprite and animates it.
In my scene I have a global property for flag
class GameScene: SKScene {
var flag: Flag!
func didMoveToView...
}
Why would this create a strong reference if flag itself doesnt reference the GameScene?. Also I can't use
weak var flag: Flag!
because I get a crash once the flag gets initialised.
Is there something obvious I am missing when doing this?
Is there a good trick to find them in Instruments because it seems madness for me.
It just confusing to me mainly because my classes are quite simple and dont reference other custom classes, scenes, viewControllers etc.

Thank you for all the answers, especially appzYourLift and your detailed reply.
I was digging into my spriteKit project the whole day, also experimenting with playground a lot and my game is totally leak free now with no strong reference cycles to be found.
It actually turned out a lot of the leaks/persistant classes I was seeing in instruments where simply there because something else didnt deallocate.
Its seems that repeat action forever was causing my leaks. All I had to do is loop through all the nodes in my scene and remove their actions.
This question helped me with this
iOS 7 Sprite Kit freeing up memory
UPDATE: I recently revisited this topic again because I felt like having to manually remove actions on nodes should not be necessary and could become cumbersome. After more research I found the precise issue for the (my) memory leaks.
Having a "SKAction repeat forever" like this apparently causes the leak.
let action1 = SKAction.wait(forDuration: 2)
let action2 = SKAction.run(someMethod)
let sequence = SKAction.sequence([action1, action2])
run(SKAction.repeatForever(sequence))
So you need to change it to this to not cause a leak
let action1 = SKAction.wait(forDuration: 2)
let action2 = SKAction.run { [weak self] in
self?.someMethod()
}
let sequence = SKAction.sequence([action1, action2])
run(SKAction.repeatForever(sequence))
This is coming directly from an Apple bug report I made.

Do all properties create strong reference cycles or only global properties that are created before a class is initialised?
Every strong reference to an object could become part of strong reference cycles. It could be
a strong stored property
a strong local/global variable/constant
With strong I mean it is not declared as weak or unowned.
Infact if for a given moment there is a strong reference path from an object to the object itself then the cycle is created and ARC will not free the memory for all the objects in the cycles.
What other things could potentially create a strong reference cycle in my simple classes?
There's no more to say. When the object A has a strong reference to the object B... which has a strong reference to A you have a strong reference cycle.
A few suggestions:
Keep track of the deinitialization
Just add a deinitializer to your class and check on the console that your objects do get deinitialized when you expect it
class Foo: SKSpriteNode {
deinit {
print(String(self))
}
}
Let SpriteKit manage the strong references
Avoid strong references between nodes in your scene graph. Use instead the methods and computed properties provided by the SKNode class to retrieve other nodes like.
.scene
.parent
.childNodeWithName(...)
.childrean
This approach could lead to some performance issue but you should first make you code correct and then try to improve the performance.

Related

Assets singletons and reference cycles

I currently have an Assets singleton class that provides me access to textures, sounds, and music. As my partner and I are going through the memory management stage of our project we have realized that we may have a serious leak being created, and based on my use of Xcode instruments our biggest issue may center around this singleton class. While there are certainly other leaks present, we have noticed that when moving between map screen and game screen back and forth, there is a fairly steady increase of ~100 mb, which appears to correspond to our 11 map assets. With that context, my question is this:
Would the code below create a retain cycle, and if so, can it be managed with the existence of the singleton class, or ought we break this thing up s.t. texture atlases are held separately?
func transitionToMapScreen()
{
//I hope this isn't necessary eventually, but we were trying to ensure all game textures and emitters were deallocated
Assets.sharedInstance.deallocateGameAssets()
gameScene = GameScene()
Assets.sharedInstance.preloadMap
{
[unowned self] in
let mapScene = MapScreen(fileNamed: "MapScreen")!
mapScene.preCreate()
mapScene.scaleMode = self.scaleMode
// Transition with a fade animation
let reveal = SKTransition.fade(withDuration: 2.0)
let fadeMusic = SKAction.run
{
Assets.sharedInstance.bgmTitlePlayer?.setVolume(1.0, fadeDuration: 1.0)
Assets.sharedInstance.bgmTitlePlayer?.play()
Assets.sharedInstance.bgmGamePlayer?.setVolume(0.0, fadeDuration: 1.0)
}
let stopGameMusic = SKAction.run
{
Assets.sharedInstance.bgmGamePlayer?.stop()
}
let transitionAction = SKAction.run
{
self.view?.presentScene(mapScene, transition: reveal)
}
self.run(SKAction.sequence([SKAction.wait(forDuration: 1.0), fadeMusic, SKAction.group([stopGameMusic, transitionAction])]))
} // end Assets.sharedInstance.preloadMap completion block*/
}
From what I understand about retain cycles in Swift, isn't this creating a self reference to the Assets class and creating a memory leak? And could this explain the behavior of our map assets being retained in memory? And if so, what is the proper method of managing this?
I wanted to post this here for those who might be looking for an answer to a similar problem related to hunting down retain cycles to explain memory growth issues. First, thanks very much to all those who helped me halt my neurons' random frantic attempts to find retain cycles where there were none (or were not ones big enough to matter). Now:
First, retain cycles are scary, sure, but using instruments to find them and then managing as Apple recommends since Swift 4.2 is appropriate:
something()
{
[weak self] in
guard let self = self else { return }
self.whatever()
}
I've seen some people argue that you should determine whether unowned or weak makes sense -- this honestly takes the guesswork out and is much easier. I did find unowned crashes are rare anyway at least for us, but I won't opine on your app, and this solves things. Now, then, once you have cleaned house:
We discovered that our memory growth issues stemmed not from our asset singleton class inherently but rather the sheer size of our texture atlases and corresponding overlapping usage of those atlases. I cannot recommend this discussion enough: How does SKTexture caching and reuse work in SpriteKit?. That will explain conceptually the issues you might face with atlases better than I can.
Summarily, however: SpriteKit manages the allocation of your texture atlases, and accordingly you must understand that if you have a very large atlas that is frequently loaded it may not manage it as you expect (I still don't have enough detail to describe that in a better way, but as I said please reference the discussion above as well as Apple's developer guide on SKTextureAtlas: https://developer.apple.com/documentation/spritekit/sktextureatlas).
Now, related to that Apple discussion, I noted this line, which I think really ought to be bolded and in red: "SpriteKit implicitly loads an atlas when one of the atlas's textures is accessed." This was critical to solving what I think was our underlying issue: we had a few places where we accessed textures in the atlas via a single instance for some reason -- you must recognize that in the case of a large atlas SpriteKit will then load your entire massive atlas into memory. So I no longer take lightly Apple's note to manage your atlas sizes. Atlases are meant for assets that are always used together and are going to be drawn together. Loading disparate assets into an atlas was our mistake. We are reorganizing how we manage these accordingly.

how to fix memory leaks with xcode (Swift 3)

i've made a game in Swift and it has a single and a multiplayer modes, and it seems that i have a problem with the memory management, because the app occupies 150 MB and i have no idea why.
the memory debugger of xcode shows that there are 15 issues when i choose a multiplayer game, but in the single player mode it semmes that there are no leaks and still it got 150 MB usage.
here are a screenshot of the debugger in action and i'll appreciate any help with all these triangles, circles and rombs.
thanks in advance!
This looks like a strong reference cycle.
Usually this happens when you don't declare objects that you use in closures as weak. In order to understand what a strong reference cycle is, check the following resources out:
Blog post about strong reference cycles
WWDC videos available (e.g. here and here)
Although the WWDC videos are a little bit older, the main idea is still the same. At least two objects are (transitively) holding a strong reference to each other. When both get released (e.g. when a GameScene is deallocated) they still point to one another, so the system is unable to deallocate them.
Usually, you get a strong reference cycles in the context of closures:
myMethod(...) { (param1, param2) in
self.myVariable = ...
}
If you have a structure like this in your code, try to make self weak. i.e.
myMethod(...) { [unowned self] (param1, param2) in
self.myVariable = ...
}
Since I don't see the code, I can't exactly see where the problem is but it seems that you have a strong reference cycle in your dictionaries. Then it might not be a closure problem.

How to duplicate a sprite in sprite kit and have them behave differently

I've been working on a IOS mobile game and I wanted to take an object,duplicate it, and have the copies move all over the screen. I've looked through Google to find things relevant to this but they were all in Objective C or just didn't have what I was looking for. I want to know how to do this in Swift and SpriteKit.
If you are working with SKSpriteNode you can copy it and all it's current properties with:
let copiedNode = nodeToCopy.copy() as! SKSpriteNode
You will still need need to add copiedNode to your scene. copiedNode will also continue to run any actions that nodeToCopy was running. You can cancel them with copiedNode.removeAllActions().
Note that the documentation for the protocol NSCopying reads exactly:
Protocol
NSCopying
A protocol that objects adopt to provide functional
copies of themselves.
The exact meaning of “copy” can vary from class to class, but a copy
must be a functionally independent object with values identical to the
original at the time the copy was made...
Indeed, in the case of SKSpriteNode, Apple have interpreted that idea so that the copy() function "spawns" another instance of the item, exactly as in any game engine.
(So, for SKSpriteNode copy() works identically to the sense of Instantiate in Unity, say.)
As mogelbuster points out below, there is nowhere in the Apple documentation that they state "The spawn command in Apple is copy()" but in fact they have interpreted this "The exact meaning of “copy” can vary from class to class" in exactly that way for SKNode, since indeed it's a game engine and it's the only meaningful sense of copy there.
It's worth noting that the most completely typical way to work in games is: for your say rocketShips, you would have one "model" of your rocketShip, say modelRocketShip. The model simply sits offscreen, or is perhaps marked as invisible or inactive. You never use the model in the game, it just sits there. When you spawn rocketShips, you just dupe the model. (So in Apple that's modelRocketShip.copy() and then set the position etc.)
You can define a function to create and return a sprite :
func createSprite()->SKSpriteNode{
let sprite = SKSpriteNode(...)//Use the init function in the SKSpriteNode class
//Add some code to define the sprite's property
return sprite
}
And call this function to get some sprites that have the same property:
let spriteOne = createSprite()
let spriteTwo = createSprite()
Then you can add different SKActions to each of them so that they can behave differently.
Once you have multiple SKSpriteNodes, you can also control them by using EnumerateChildNodesWithName (assuming all your nodes have the same name) to go through all of them do do what you want in the update() function.
On a more advanced level, you could subclass SKSpriteNode and incorporate your own behaviour in your custom class.

Memory problems when switching between scenes SpriteKit

Ok so awhile back in testing I had a random crash with no error, and I have no clue why. So i went into analyze things and I came up with the following data.
As it would appear my memory usage is getting higher and higher and higher until it sorta plateus. Notice how at the beginning the slope of the general curvature is greater then later on. (as you might notice this is my first time going in and analyzing this sort of thing).
Now what happens in the game is that basically their are two screens.
1. Menu: this screen has quite a few textures but does nothing except has button to play game
2. Game: this has TOOONS of textures and has the bulk of the cpu usage because its the actual game.
3. Death: This screen has one asset, and it is a button that allows you to replay the game. This should not be using much memory OR cpu. However it still has memory. To me this screams whatever a "memory leak" is, is going on.
If you will look at the chart basically what was going on in the game was the menu started, and the first spike was loading up the actual game, then I died. Then from then on I was switching between the Game and Death screens, each spike signals the Game scene being loaded.
If this data were the way I would predict it you would se an oscillation between a very small memory use for the death screen, and then a return to a game memory usage.
Moral of the story is I am pretty sure that sprite kit isn't properly cleaning up after switching scenes and I need to know why if possible.
Btw in order to switch scenes I am using the method made by maxkargin detailed
here
BTW I am working in swift with sprite kit and SKScenes, and SKSpriteNodes
Thanks much!
There is a few reasons why this is, I had a similar problem with my games. If you do it correctly there is no need to remove stuff such as textures. Removing textures on every scene changes is also not ideal, you want to keep them in memory for performance so they do not have to be reloaded each time.
Here is a basic checklist you can use to see if you create a memory leak.
1) Add the deinit method with a print statement to each scene/class. If deinit gets called your scene deallocated correctly.
deinit {
print("Deinit GameScene")
}
2) Are you creating strong reference cycles somewhere by creating references between 2 classes?
The classic Apple example of a strong reference cycle
class Person {
var dog: Dog?
}
class Dog {
var person: Person?
}
To fix it you would have to make 1 of those 2 properties weak
class Person {
var dog: Dog?
}
class Dog {
weak var person: Person?
}
Also good practice with optionals is to set them to nil when they are no longer needed.
person = nil
Maybe check google and the Apple Swift documentation on how to deal with this. I also asked a similar question a while back
Swift SpriteKit ARC for dummies
3) Are you using closures? They can cause memory leaks.
A more common scenario in SpriteKit is these 2 examples which could/will cause a memory leak and makes your scene to not deallocate. (action 2 is a closure which captures self)
// Example 1
let action1 = SKAction.wait(forDuration: 1)
let action2 = SKAction.run(someMethod)
let sequence = SKAction.sequence([action1, action2])
run(SKAction.repeatForever(sequence))
// Example 2
let action1 = SKAction.wait(forDuration: 1)
let action2 = SKAction.run {
self.someMethod()
}
let sequence = SKAction.sequence([action1, action2])
run(SKAction.repeatForever(sequence))
A good rule of thumb is that when the compiler forces you to use self than you most likely will create a memory leak without using weak/unowned.
So to fix the above 2 SKAction examples you could either make sure you always remove all actions when you change scenes or IMO even better would be to change your code to this to avoid creating a memory leak in the first place.
let action1 = SKAction.wait(forDuration: 1)
let action2 = SKAction.run { [weak self] in
self?.someClassMethod()
}
let sequence = SKAction.sequence([action1, action2])
run(SKAction.repeatForever(sequence))
Note in all those above example you could also write
.... { [unowned self] in
and than you would not need to use the ? in the closure
self.someMethod()
When you use unowned you basically say self will never be nil which could cause a crash if it is actually nil when the closure is called. If you use weak self you tell the compiler self might become nil before the closure is called therefore self is an optional to avoid the crash.
I think it is almost always better to use "weak" instead of "unowned" to avoid this. In one of my games I was using unowned self in a closure that was called when it fetched StoreKit products from iTunes. This caused me subtile crashes because I could exit the SKScene before the closure was called. If you use weak self you will not crash because you use optionals.
Hope this helps a bit
sprite kit uses cache to retain perforemce between scenes that why switching between scenes spike up memory and this memory does not release at any case either you remove all children from scene or [self.view presentScene:nil]; their are better solution for this...
read this article carefully
https://developer.apple.com/library/ios/qa/qa1889/_index.html
manage scene with view's and remove SKView's from those view's to maintain memory in multi screen game
So based on what everyone said this is what I did. Before changing to a new scene I run functions that basically takes all textures, and sprite nodes and sets them to an empty constructor so that they are smaller, then I remove all actions, and children. This has seemed to do the trick! Their might still be other memory problems, however I need to look more carefully and tweak some things before I can be sure.
Thanks for the advice everyone!
As a side-dish answer, I had a complex scene with a lot of custom sprite kit subclasses and actions interacting together, and the only thing that prevented my SKScene subclass from calling its deinit was one delegate I forgot to define as weak or unowned, which created a classic strong reference cycle problem. So if you assigned any delegates to self in your scene's setup like this:
myCustomNode.delegate = self
Then check the definition of the delegate, it should look like this:
weak var delegate:MyCustomProtocol?
or this:
unowned var delegate:MyCustomProtocol?
If you do not have access to myCustomNode's source code, then try assigning the delegate to nil in your scene like this:
override function willMove(from view:SKView) {
myCustomNode.delegate = self
}
Also, if you get the error
weak / unowned may only be applied to class and class-bound protocol types
This is because your custom protocol must include : class like this:
protocol MyCustomProtocol : class {/*protocol definition here*/}

How does Swift memory management work?

Specifically, how does Swift memory management work with optionals using the delegate pattern?
Being accustomed to writing the delegate pattern in Objective-C, my instinct is to make the delegate weak. For example, in Objective-C:
#property (weak) id<FooDelegate> delegate;
However, doing this in Swift isn't so straight-forward.
If we have just a normal looking protocol:
protocol FooDelegate {
func doStuff()
}
We cannot declare variables of this type as weak:
weak var delegate: FooDelegate?
Produces the error:
'weak' cannot be applied to non-class type 'FooDelegate'
So we either don't use the keyword weak, which allows us to use structs and enums as delegates, or we change our protocol to the following:
protocol FooDelegate: class {
func doStuff()
}
Which allows us to use weak, but does not allow us to use structs or enums.
If I don't make my protocol a class protocol, and therefore do not use weak for my variable, I'm creating a retain cycle, correct?
Is there any imaginable reason why any protocol intended to be used as a delegate protocol shouldn't be a class protocol so that variables of this type can be weak?
I primarily ask, because in the delegation section of the Apple official documentation on Swift protocols, they provide an example of a non-class protocol and a non-weak variable used as the delegate to their class:
protocol DiceGameDelegate {
func gameDidStart(game: DiceGame)
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
Should we take this as a hint that Apple thinks we should be using structs as delegates? Or is this simply a bad example, and realistically, delegate protocols should be declared as class-only protocols so that the delegated object can hold a weak reference to its delegate?
Should we take this as a hint that Apple thinks we should be using structs as delegates? Or is this simply a bad example, and realistically, delegate protocols should be declared as class-only protocols so that the delegated object can hold a weak reference to its delegate?
Here's the thing. In real life Cocoa programming, the delegate is likely to be an existing class. It is a class because it exists for some other purpose that only a class can satisfy - because Cocoa demands it.
For example, very often, to take iOS as an example, one view controller needs to act as another view controller's delegate for purposes of arranging a message back and forth between them. Ownership of the view controllers is dictated by the view controller hierarchy and by nothing else. So, in Swift, just as in Objective-C, you had better make that delegate property weak, because it would be terrible if one view controller suddenly took memory management ownership of another view controller!
So, in the real world of the Cocoa framework, there is serious danger of incorrect ownership or of a retain cycle. And that is the problem that weak solves. But it only works, as you rightly say, with classes.
The example in the book, however, is about some objects living off in an abstract made-up artificial Swift-only world. In that world, as long as you aren't in danger of circularity (retain cycle), there's no reason not to use structs and there's no reason to worry about memory management. But that world is not the world you will usually be programming in! And that world is not the framework Cocoa world that your Objective-C delegate pattern comes from and belongs to.
Yes, this example is a bit of an oddity.
Because the example uses a non-class protocol type, it has to expect a possible struct implementing the protocol, which means that the DiceGame instance owns its delegate. And indeed, that violates typical assumptions about the delegate pattern.
It doesn't lead to a reference cycle in this case because the DiceGameTracker is a fictional object that doesn't own the DiceGame itself — but in a real-world app it's possible, even likely, that a delegate might also be the owner of the delegating object. (For example, a view controller might own the DiceGame, and implement DiceGameDelegate so it can update its UI in response to game events.)
That kind of reference cycle would probably turn into a confusing mess if either the game, its delegate, or the type implementing one or both of those protocols were a value type — because value types are copied, some of the variables in your setup would end up being distinct copies of the game (or game's owner).
Realistically one would expect to use reference types (classes) to implement this anyway, even if the protocol declaration leaves open the possibility of doing it otherwise. Even in a hypothetical Swift-only world, it probably makes sense to do it that way... generally whenever you have something with long life, internal mutable state, and a usage pattern that has it being accessed by potentially multiple other actors, you want a class type, even if you can sort of fake otherwise with value types and var.
If you must have structs and emums in your protocol, then if you make your delegate nil just before you close the view controller, then this breaks the retain cycle. I verified this in Allocations in Instruments.
// view controller about to close
objectCreatedByMe.delegate = nil
It's an optional, so this is valid.