Consider the following use case:
In a model for some game, you have a Player class. Each Player has an unowned let opponent: Player which represents the opponent they are playing against. These are always created in pairs, and a Player must always have an opponent since it is non-optional. However, this is very difficult to model, since one player must be created before the other, and the first player will not have an opponent until after the second one is created!
Through some ugly hacking, I came up with this solution:
class Player {
private static let placeholder: Player = Player(opponent: .placeholder, name: "")
private init(opponent: Player, name: String) {
self.opponent = opponent
self.name = name
}
unowned var opponent: Player
let name: String
class func getPair(named names: (String, String)) -> (Player, Player) {
let p1 = Player(opponent: .placeholder, name: names.0)
let p2 = Player(opponent: p1, name: names.1)
p1.opponent = p2
return (p1, p2)
}
}
let pair = Player.getPair(named:("P1", "P2"))
print(pair.0.opponent.name)
print(pair.1.opponent.name)
Which works quite well. However, I am having trouble turning opponent into a constant. One solution is to make opponent a computed property without a set, backed by a private var, but I'd like to avoid this.
I attempted to do some hacking with Swift pointers, and came up with:
class func getPair(named names: (String, String)) -> (Player, Player) {
var p1 = Player(opponent: .placeholder, name: names.0 + "FAKE")
let p2 = Player(opponent: p1, name: names.1)
withUnsafeMutablePointer(to: &p1) {
var trueP1 = Player(opponent: p2, name: names.0)
$0.moveAssign(from: &trueP1, count: 1)
}
return (p1, p2)
}
But this gives a segfault. Furthermore, when debugging with lldb, we can see that just after p1 is initialized, we have:
(lldb) p p1
(Player2.Player) $R3 = 0x0000000101004390 {
opponent = 0x0000000100702940 {
opponent = <uninitialized>
name = ""
}
name = "P1FAKE"
}
But at the end of the function, lldb shows this:
(lldb) p p1
(Player2.Player) $R5 = 0x00000001010062d0 {
opponent = 0x00000001010062a0 {
opponent = 0x0000000101004390 {
opponent = 0x0000000100702940 {
opponent = <uninitialized>
name = ""
}
name = "P1FAKE"
}
name = "P2"
}
name = "P1"
}
(lldb) p p2
(Player2.Player) $R4 = 0x00000001010062a0 {
opponent = 0x0000000101004390 {
opponent = 0x0000000100702940 {
opponent = <uninitialized>
name = ""
}
name = "P1FAKE"
}
name = "P2"
}
So p1 correctly points to p2, but p2 still points to the old p1. What's more, p1 has actually changed addresses!
My question is two-fold:
Is there a cleaner, more 'Swifty' way to create this structure of mutual non-optional references?
If not, what am I misunderstanding about UnsafeMutablePointers and the like in Swift that makes the above code not work?
I think an implicitly unwrapped optional is what you want. You declare it with an exclamation mark (!). It's a promise to the compiler that even though the property may be initialized during the init call, it will have a valid value when you use it. Combining this with a private setter, you can achieve what you want:
class Player: CustomStringConvertible {
var name: String
private(set) weak var opponent: Player!
init(name: String) {
self.name = name
}
class func getPair(named names: (String, String)) -> (Player, Player) {
let p1 = Player(name: names.0)
let p2 = Player(name: names.1)
p1.opponent = p2
p2.opponent = p1
return (p1, p2)
}
var description: String {
return self.name
}
}
let (p1, p2) = Player.getPair(named: ("Player One", "Player Two"))
print(p1.opponent) // Player Two
print(p2.opponent) // Player One
Since the setter is private the compiler will throw an error if you try to change it:
let p3 = Player(name: "Player Three")
p1.opponent = p3 // Error: Cannot assign to property: 'opponent' setter is inaccessible
Note that since you intended for getPair to be the single method to create a Player instance, you may as well set the init call to private since it doesn't set the opponent property:
private init(name: String) {
// ...
}
After messing with this for a while it seems what you're wanting to do is likely not possible, and doesn't really jive with Swift. More importantly, it's likely a flawed approach to begin with.
As far as Swift goes, initializers are required to initialize all stored values before they return. This is for a number of reasons I won't go into. Optionals, IUOs, and computed values are used when a value cannot be guaranteed/calculated at initialization. If you don't want Optionals, IUOs or computed values but still want some stored values to be unset after init, you're wanting to have your cake and eat it too.
As far as the design, if you need two objects to be linked so tightly as to require each other at initialization, your model is (IMO) broken. This is the exact problem hierarchical data structures solve so well. In your specific example, it seems clear you need some sort of Match or Competition object that creates and manages the relationship between the two players, I know your question is closer to "is this possible" not "should it be done", but I can't think of any situation where this isn't a bad idea. Fundamentally it breaks encapsulation.
The Player object should manage and track the things that exist within a Player object, and the only managed relationships within the Player class should be with it's children. Any sibling relationship should be accessed/set by it's parent.
This becomes a clearer problem with scale. What if you want to add a third player? what about 50? You will then have to initialize and connect every single player to ever other before you can use any of the players. If you want to add or remove a player, you will have to do it for every single connected player simultaneously, and block anything from happening while that takes place.
Another issues is it makes it unusable in any other situation. If designed properly, a Player could be used in all types of games. Whereas the current design allowed it to be used only in a 1v1 situation. For any other case you would have to re-write it and your code base would diverge.
In summation, what you want is probably not possible in Swift, but if or when it becomes possible, it's almost certainly a bad idea anyway :)
Sorry for the essay, hope you find it helpful!
There is a way to do this cleanly in Swift using lazy properties (for a convenient API) and a container that contains both players (for sane memory management). For a TL;DR, take a look at the example code below. For a longer answer, read on:
By definition, a cycle between two objects must be optional in nature in Swift because:
Swift dictates that all fields of an object need to be initialized by the time the the object's initializer has executed. So, an optional, or an implicitly unwrapped optional reference, or an unowned reference are your options if you want to tie together two objects with references (both need initialization, so at least one exists before its opponent).
If the objects are of a class type, then they should be weakly referenced, and again, weak references are by definition optional in nature (either automatically zeroed and implicit or explicit).
Being able to create a pair of dynamically allocated objects like what you are after is really more natural in an environment with a garbage collector (Swift uses automated reference counting which simply leaks your pair of objects if it goes unrooted from your code). Some kind of container that contains both players is therefore useful (if not absolutely necessary) in Swift.
I would argue that even despite the language limitations which prevent you from doing what you are trying on initialization time, your model has other problems which would benefit from a hierarchy of two levels.
If a player only ever exists in context of another player, you should only ever be able to create a maximum of two of them per match.
You may want to also define an order to the players, for instance to decide who starts if it is a turn based game, or to define for presentation purposes one of the players as playing a "home" match, etc.
Both of the above concerns, especially the first, is really pointing clearly to the utility of some kind of container object that would handle the initialization of your Players (i.e. only that container would know how to initialize a Player, and would be able to bind all the mutable properties together). This container (Match) in the below example code is one where I placed an opponent(for:Player) method to query for the opponent for a player. This method gets called in the lazy opponent property of Player.
public class Match {
public enum PlayerIndex {
case first
case second
}
private(set) var players:PlayerPair
init(players:PlayerNamePair) {
// match needs to be first set to nil because Match fields need setting before 'self' can be referenced.
self.players = (Player(match: nil, name: players.A, index: .first),
Player(match: nil, name: players.A, index: .second))
// then set the 'match' reference in the Player objects.
self.players.A.match = self
self.players.B.match = self
}
public func opponent(for player:Player) -> Player {
switch (player.index) {
case .first:
return self.players.B
case .second:
return self.players.A
}
}
/* Player modelled here as a nested type to a Match.
* That's just a personal preference, but incidental to the question posted. */
typealias PlayerNamePair = (A:String, B:String)
typealias PlayerPair = (A:Player, B:Player)
public class Player {
public let name:String
fileprivate let index:PlayerIndex
fileprivate weak var match:Match?
/* This init method is only visible inside the file, and only called by Match initializer. */
fileprivate init(match:Match?, name:String, index:PlayerIndex) {
self.name = name
self.match = match
self.index = index
}
/* We dare implicitly unwrap here because Player initialization and lifecycle
* is controlled by the containing Match.
*
* That is, Players only ever exists in context of an owning match,
* therefore it's OK to treat it as a bug which crashes reproducibly
* if you query for the opponent for the first time only after the match (which we know to have been non-nil) has already been deallocated. */
public lazy var opponent:Player = public lazy var opponent:Player = self.match!.opponent(for: self)
}
}
I was trying to do something like this (it is a contrived example for demonstration purposes only):
class Test {
let hello = "hello"
let world = "world"
let phrase: String {
return self.hello + self.world
}
}
but you can't use let for computed properties in Swift. Is there a way to do this without having to write an init() method? Thanks!
The reason let doesn't work on a read-only calculated property is because it's used to state that the property's actual value will never change after being set – not that the property is read-only. As the Apple docs say (emphasis mine):
You must declare computed properties — including read-only computed
properties — as variable properties with the var keyword, because their
value is not fixed. The let keyword is only used for constant
properties, to indicate that their values cannot be changed once they
are set as part of instance initialization.
You therefore need to use var in order to reflect the fact that a calculated property's value could change at any time, as you're creating it on the fly when accessing it. Although in your code, this can't happen – as your hello and world properties are let constants themselves. However, Swift is unable to infer this, so you still have to use var.
For example:
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
(This doesn't change the readability of the property – as because you haven't provided it with a setter, it's still read-only)
However in your case, you might want to consider using a lazy property instead, as your hello and world properties are constants. A lazy property is created when it's first accessed, and keeps its value for the rest of its lifetime – meaning you won't have to keep on concatenating two constants together every time you access it.
For example:
class Test {
let hello = "hello"
let world = "world"
lazy var phrase: String = {
return self.hello + self.world
}()
}
Another characteristic of let properties is that their value should always be known before initialisation. Because the value of a lazy property might not be known before then, you also need to define it as a var.
If you're still adamant on wanting a let property for this, then as far as I can see, you have two options.
The first is the neatest (although you've said you don't want to do it) – you can assign your phrase property in the initialiser. As long as you do this before the super.init call, you don't have to deal with optionals. For example:
class Test {
let hello = "hello"
let world = "world"
let phrase: String
init() {
phrase = hello+world
}
}
You simply cannot do it inline, as self at that scope refers to the static class, not an instance of the class. Therefore you cannot access the instance members, and have to use init() or a lazy/calculated property.
The second option is pretty hacky – you can mirror your hello and world properties at class level, so you can therefore access them inline in your phrase declaration. For example:
class Test {
static let hello = "hello"
static let world = "world"
// for some reason, Swift has trouble inferring the type
// of the static mirrored versions of these properties
let hello:String = Test.hello
let world:String = Test.world
let phrase = hello+world
}
If you don't actually need your hello or world properties as instance properties, then you can just make them static – which will solve your problem.
Yes to make it work as computed properties, replace let to var.
Like,
class Test {
let hello = "hello"
let world = "world"
var phrase: String {
return self.hello + self.world
}
}
This way you can use it without init()
Xcode is dropping an error and I dont know how to solve it. Maybe someone can give me a hand or a good hint?
Here is my code that is working within my Game Scene:
var currentLevel: Int = 1
var platforms: PlatformsNode!
var platformNode1: PlatformNode!
var platformNode2: PlatformNode!
var platformNode3: PlatformNode!
var platformArray = [SKNode]()
let passenger = SKSpriteNode(imageNamed: "passenger")
var passengerNumber:Int = 1
var startPlatform = [3, 3, 2, 1, 2]
var destinationPlatform = [1, 1, 3, 2, 1]
// Level selection
class func level(levelNum: Int) -> GameScene? {
let scene = GameScene(fileNamed: "Level\(levelNum)")! // <- compiler error
scene.currentLevel = levelNum
scene.scaleMode = .AspectFill
return scene
}
As soon as I want to replace
let passenger = SKSpriteNode(imageNamed: "passenger")
with let passenger: PassengerNode! the compiler drops an compiler error "Game Scene cannot be constructed because it has not accessible initializers".
And this error just shows up, when I change the way I want to declare let passenger.
The reason I want to change it, is that I want to replace it with a class, so that the passenger
a. can have a different type with a different texture (this can may be solved differently)
b. can be removed from the parent and later be added again - with hopefully no error.
You got any ideas? I am really stuck somehow :-?
declaring passenger with only its type and no initial value requires that you add an init() to your class because the compiler does not know what initial value to give it when the object is created.
You can either create an init() and set a value to passenger or delare it as
let passenger: PassengerNode! = nil
(this assumes that your code does not reference the variable before making sure to put something in it or checks for nil/optional before using it).
I am learning Swift and noticed there are different ways to declare a variable.
Questions: What is the difference between the following 3 lines, and when to use which?
var mySprite: SKSpriteNode?
var mySprite = SKSpriteNode()
var mySprite = SKSpriteNode(imageNamed: String())
Similarly, what is the difference between the following 3 lines and when to use which?
var myLabel: SKLabelNode?
var myLabel = SKLabelNode()
var myLabel = SKLabelNode(fontNamed: "name")
var mySprite: SKSpriteNode?
The type of your variable is optional in this case which means it is nil at the time you create an instance of a class containing this variable, which is a property of your class.
var mySprite = SKSpriteNode()
Here you initialize your property at the time you make an instance of your class which contains it.
var mySprite = SKSpriteNode(imageNamed: String())
Similar as number two. But different in the initialization method used. Init().
There is no difference in 2 last lines in both cases in terms of declaring a variable.
First declaration differs from last two in that it is declaring an optional value of type SKLabelNode with a default value of nil.
The first line of code will declare the variable as
Optional
. Optional variable in swift means that it could be nil or it could have a value, and you always should safely unwrap it using the "if let" expression.
the second and the third line will define a new object of the class SKSpriteNode or SKLabelNode but each with different parameters.
I'm making a small game in swift and want to make my player move across 3 fixed points on the screen.
I created an array to store the values in which I calculate in the init function, but the compiler doesn't agree with me not declaring the array before super.init(). Here's what I have
//properties
var player:SKSpriteNode = SKSpriteNode()
var playerPos = 1
var legalPositions: [CGFloat]
override init(size:CGSize)
{
super.init(size: size)
legalPositions[0] = self.frame.width/4
legalPositions[0] = self.frame.width/2
legalPositions[0] = self.frame.width/0.5
}
When I try to build it I get the error
GameScene.swift:31:9: Property 'self.legalPositions' not initialized at super.init call
Is there a way to initialize the property before calling the init function?
This is actually a good use case for Implicitly Unwrapped Optionals. Basically you'd define legalPositions as [CGFloat]! which will default it to nil. Then you can set it after your call to super.init(size: size).
Since it's implicitly unwrapped, you don't have to worry about unwrapping it with optional binding or forced unwrapping each time you use it. And, since you're always setting it in your init method, you don't have to worry about it being nil and causing a crash.
//properties
var player:SKSpriteNode = SKSpriteNode()
var playerPos = 1
var legalPositions: [CGFloat]!
override init(size:CGSize)
{
super.init(size: size)
legalPositions[0] = self.frame.width/4
legalPositions[0] = self.frame.width/2
legalPositions[0] = self.frame.width/0.5
}
Yes, you can use a dummy value that gets overwritten in the initializer:
var legalPositions: [CGFloat] = [0]
That way, the legalPositions array is initialized before you're calling the base class initializer. Swift's two-phase initialization requires this process in order to be able to guarantee that all properties are initialized after creating a new instance of a class.
The benefit of this approach is that there's no need for implicitly unwrapped optionals. They open up the possibility for nil values being set, which is not what you want.
Note that the first two assignments in your snippet are unnecessary since the third one will overwrite the value of the first item of the legalPositions array anyway.