Structs that refer to each other in Swift 3
The problem is that an Optional
stores its Wrapped
value inline (see Mike Ash's fantastic blog post for more info about this) – meaning that an Optional
instance (regardless of whether it is nil
or not) will occupy at least the same amount of memory as the type you wish to store in its .some
case (the Wrapped
type).
Thus, as your Pin
struct has a property of type DetailedPin?
, and DetailedPin
has a property of type Pin?
, infinite storage would be required in order to store these values inline.
The solution therefore is simply to add a layer of indirection. One way of doing this would be to make Pin
and/or DetailedPin
a reference type (i.e a class
) as @dfri has suggested.
However, if you wish to keep the value semantics of Pin
and DetailedPin
, one option would be to create a wrapper type backed by a class instance to provide the necessary indirection:
/// Provides indirection for a given instance.
/// For value types, value semantics are preserved.
struct Indirect<T> {
// Class wrapper to provide the actual indirection.
private final class Wrapper {
var value: T
init(_ value: T) {
self.value = value
}
}
private var wrapper: Wrapper
init(_ value: T) {
wrapper = Wrapper(value)
}
var value: T {
get {
return wrapper.value
}
set {
// Upon mutation of value, if the wrapper class instance is unique,
// mutate the underlying value directly.
// Otherwise, create a new instance.
if isKnownUniquelyReferenced(&wrapper) {
wrapper.value = newValue
} else {
wrapper = Wrapper(newValue)
}
}
}
}
You can now just use the Indirect
wrapper for one (or both) of your structs properties:
struct DetailedPin {
private var _pin = Indirect<Pin?>(nil)
// Convenience computed property to avoid having to say ".value" everywhere.
var pin: Pin? {
get { return _pin.value }
set { _pin.value = newValue }
}
}
struct Pin {
var detailedPin: DetailedPin?
var foo: String
}
var d = DetailedPin()
var p = Pin(detailedPin: d, foo: "foo")
d.pin = p
// testing that value semantics are preserved...
var d1 = d
d1.pin?.foo = "bar"
print(d.pin?.foo as Any) // Optional("foo")
print(d1.pin?.foo as Any) // Optional("bar")
Swift pass struct by reference?
Structs can be passed by reference using the inout
keyword and the &
operator.
struct Test {
var val1:Int
let val2:String
init(v1: Int, v2: String) {
val1 = v1
val2 = v2
}
}
var myTest = Test(v1: 42, v2: "fred")
func change(test: inout Test) {
// you can mutate "var" members of the struct
test.val1 = 24
// or replace the struct entirely
test = Test(v1: 10, v2: "joe")
}
change(test: &myTest)
myTest // shows val1=10, val2=joe in the playground
This practice is discouraged unless you can prove it's the only way to get the performance you need in a critical situation.
Note that you won't save the burden of copying the UIImage by doing this. When you put a reference type as a member of a struct, you still only copy the reference when you pass it by value. You are not copying the contents of the image.
Another important thing to know about struct performance is copy-on-write. Many built in types like Array are value types, and yet they're very performant. When you pass around a struct in Swift, you don't undergo the burden of copying it until you mutate it.
Check out the WWDC video on value types to learn more.
Passing a value type as reference in Swift
struct
s are always passed by value. The whole point of using a struct
is to have it behave as a value type. If you need delegation (which usually implies mutable state), you should be using a class.
If you really really need to, you could force pass-by-reference by using an inout
parameter, but that is not recommended in general. You could also use a box type to simulate passing by reference. But, in general, you should just use a class if you need reference behavior.
Swift 3 - Structs in a Collection
extension Collection where Iterator.Element: Person
restricts Iterator.Element
to types which adopt the protocol Person
or are a subclass of Person
. Both is not possible withstruct Person
, and in the full compiler log you'll find
error: type 'Iterator.Element' constrained to non-protocol type 'Person'
What you probably mean is
extension Collection where Iterator.Element == Person
which restricts the extension to collections of Person
.
Alternatively, define a protocol
protocol HasAge {
var age: Int { get }
}
adopt that by Person
struct Person: CustomDebugStringConvertible, Hashable, HasAge { ... }
and define the extension for collections of elements which have a age:
extension Collection where Iterator.Element: HasAge { ... }
Swift Tuples - Different from struct and from each other?
I find it's easiest to conceptualize Swift Tuples as "Anonymous Structs" with a few critical differences. They behave similarly, but a struct has a formal definition and allows more control over mutability, while tuples allow for pattern matching.
Similarities Between Tuples and Structs
- Both may have any number of members of any type, including closures
- Both can be constructed inline (see
typealias
in the code below) - Both prevent mutation of any members if declared as constants
- If a tuple has labeled members, both structs and tuples allow member access by label
Differences Between Tuples and Structs
- Structs require a definition before use
- Structs do not allow pattern matching against their members
- Structs allow mutability of members declared as variables if the instance is a variable
- Tuples do not allow mutating functions or functions that refer to any of its members
- Tuples may not implement Protocols
- If a tuple has anonymous members, its members can be accessed by index, unlike structs
Some code for a playground illustrating these differences and similarities
// All commented code causes a compilation error. Uncomment to view error messages.
struct StructureX {
let a: Int = 0
var b: String = "string"
}
//
// Struct member variability
//
var structureA: StructureX = StructureX()
let structureB: StructureX = StructureX()
//structureA.a = 2 // declared as a constant, instance is variable
structureA.b = "allowed" // declared as a variable, instance is variable
//structureB.a = 2 // declared as constant, instance is constant
//structureB.b = "not allowed" // declared as constant, instance is constant
structureA = structureB // these are the same type
structureA
//
// A tuple can't be used as a literal to construct a struct.
//
//let StructureC: StructureX = (a: 17, b: "nope")
//
// Typealias a labeled tuple and it can be constructed similarly to a struct
//
typealias StructureT = (a: Int, b: String)
var structureD: StructureT = StructureT(a: 0, b: "asdf")
structureD
//structureD = structureA // but they are distinct types
let emptyTuple: () = () // philosophically, isn't this the definition of Void?
print(emptyTuple) // prints as ()
let single: (Int) = (23)
//let namedSingle: (a: Int) = (a: 42)
//
// Tuple Labeled Member Access
//
var labeledTupleA: (a: Int, b: String) = (a: 0, b: "string")
labeledTupleA.0 = 5
labeledTupleA.a
labeledTupleA
var check: (a: Int, b: String)
check = labeledTupleA // same type
check
//
// Tuples can have functions/closures
//
let labeledTupleB: (Int, String, fun: () -> Void) = (0, "string", { () -> Void in
print("hi")
})
labeledTupleB.1
labeledTupleB.fun()
//labeledTupleB.0 = 10 // this tuple is a constant, so all of its members are constant
//
// Tuples with members of the same type, but differet labels are not of the same type
//
var labeledTupleC: (c: Int, d: String) = (c: -1, d: "fail")
//labeledTupleC = labeledTupleA
//labeledTupleC = labeledTupleB
//
// Tuples with anonymous members matching the type pattern of a labeled member tuple are of equivalent type
//
var unlabeledTuple: (Int, String) = (0, "good")
unlabeledTuple = labeledTupleA
unlabeledTuple = labeledTupleC
//
// Tuples with closures may not refer to sibling members
//
var labeledTupleD: (de: Int, df: (Int) -> Void) = (de: 0, df: { (num: Int) -> Void in
//de += num
//self.de += num
print(num)
})
labeledTupleD.de
labeledTupleD.df(1)
//
// Tuples allow pattern matching, Structs do not
//
//switch structureA {
//case (let i, let s):
// print(i, s)
//default:
// break
//}
switch labeledTupleD {
case (_, let closure):
closure(123)
default:
break
}
What are the main differences between classes and structs in swift 3?
class Car {
var name: String
init(name:String){
self.name = name
}
}
var carA = Car(name: "BMW")
var carB = carA
//now I will change carB
carB.name = "Nissan"
print(carA.name) //This will print Nissan
struct CarStruct {
var name: String
init(name: String){
self.name = name
}
}
var carC = CarStruct(name: "BMW")
var carD = carC
//now I will change carB
carD.name = "Nissan"
print(carC.name) //This will print BMW
As you can see both CarA
and CarB
are pointing to the same reference so if one changes the other change because the reference is changing, while in CarC
and CarD
which are structs they are copies of each other each with its value.
Swift 3 - Pass struct by reference via UnsafeMutableRawPointer?
Your function copies the data into a local struct, but does not
copy the modified data back. So this would be a possible
solution in your special case:
func testUnsafeMutablePointer(data: UnsafeMutableRawPointer?) {
var testStructInFunc = data!.load(as: TestStruct.self)
testStructInFunc.prop1 = 24
testStructInFunc.prop2 = 1.2
testStructInFunc.prop3 = false
data!.storeBytes(of: testStructInFunc, as: TestStruct.self)
}
But note that this works only if the struct contains only "simple"
values likes integers and floating point values. "Complex" types
like arrays or strings contain opaque pointers to the actual storage
and cannot be simply copied like this.
Another option is to modify the pointed-to struct like this:
func testUnsafeMutablePointer(data: UnsafeMutableRawPointer?) {
let testStructPtr = data!.assumingMemoryBound(to: TestStruct.self)
testStructPtr.pointee.prop1 = 24
testStructPtr.pointee.prop2 = 1.2
testStructPtr.pointee.prop3 = false
}
Both solutions assume that the struct still exists when the callback
is called, since passing a pointer around does not ensure the
lifetime of the pointed-to struct.
As an alternative, consider to use an instance of a class
instead.
Passing retained or unretained pointers to the instance allows to control
the lifetime of the object while the callback is "active", compare
How to cast self to UnsafeMutablePointer<Void> type in swift.
How can I refer to properties from a struct within a struct/
First of all you are mixing types with objects so you should have a type House
struct House {
let name: String
let characteristic: String
let image: Image
}
And then use that in the Wizard struct
struct Wizard {
let name: String
var house: House
}
And now you create first a House object for the Wizard and then the Wizard object
let gryffindor = House(name: "Gryffindor", characteristic: "Brave", image: Image("Lion"))
let harryPotter = Wizard(name: "Harry", house: gryffindor)
or all in one call
let harryPotter = Wizard(name: "Harry",
house: House(name: "Gryffindor", characteristic: "Brave", image: Image("Lion")))
How to deal with relationships with structs in Swift?
You were told to use structs instead of classes? You've been had. Or you just didn't understand whatever advice you got properly.
structs are value types. Classes are reference types. That means you can't have references to structs. Two structs = two different objeccts = twice the memory. You can have references to class instances.
Say you have 1000 players all playing the same game. With structs, you have 1000 copies of the game. Worse, the game would have a copy of each player, which each would have a copy of the game, which would have a copy of each player, which each would have a copy of the game, and so on forever.
That's why you make Player and Game classes.
Related Topics
Swift Update Label (With HTML Content) Takes 1Min
Cannot Invoke 'Join' with an Argument List of Type (String, [String]) in Swift 2.0
Swift 5.5: Asynchronously Iterating Line-By-Line Through a File
Why Does Swift Playground Shows Wrong Number of Executions
How to Track More Than 4 Images at a Time with Arkit
Difference Between "Precondition" and "Assert" in Swift
Set a Default Value for Uipickerview in Swift
Rxswift Map and Flatmap Difference
How to Import a Swift Function Declared in a Compiled .Swiftmodule into Another Swift File
Unsaferawpointer Assumingmemorybound VS. Bindmemory
Back Button Image - What Is It Called in Swift
What Determines Whether a Swift 5.5 Task Initializer Runs on the Main Thread
Swift, Auto Resize Custom Table View Cells
Is This a Good Way to Display Asynchronous Data
Division Not Working Properly in Swift
How to Make the Memberwise Initialiser Public, by Default, for Structs in Swift