Chapter 11: Properties
大綱
Stored properties
struct Contact {
// provide a data type for each one but opt not to assign a default value, because you plan to assign the value upon initialization.
var fullName: String
let emailAddress: String
var relationship = "Friend"
}
// Swift automatically creates an initializer for you based on the properties you defined in your structure
var person = Contact(fullName: "Grace Murray",
emailAddress: "grace@navy.mil",
relationship: "Friend")
// access the individual properties using dot notation
let name = person.fullName // Grace Murray
let email = person.emailAddress // grace@navy.mil
// assign values to properties as long as they’re defined as variables, and the parent instance is stored in a variable
person.fullName = "Grace Hopper"
let grace = person.fullName // Grace Hopper
// If you’d like to prevent a value from changing, you can define a property as a constant using "let"
person.emailAddress = "grace@gmail.com" // Error!
Default values
the automatic initializer doesn’t notice default values, so you’ll still need to provide a value for each property unless you create your own custom initializer
struct Contact {
var fullName: String
let emailAddress: String
// By assigning a value in the definition of relationship, you give this property a default value
var relationship = "Friend"
}
Computed properties
properties that are computed, which simply means they perform a calculation before returning a value
Computed properties must also include a type, because the compiler needs to know what to expect as a return value.
Computed properties don’t store any values; they return values based on calculations.
struct TV {
var height: Double
var width: Double
// Computed properties
// read-only computed property
var diagonal: Int {
let result = (height * height + width * width).squareRoot().rounded()
return Int(result)
}
var tv = TV(height: 53.93, width: 95.87)
let size = tv.diagonal // 110
Getter and setter
struct TV {
var height: Double
var width: Double
// This specificity isn’t required for read-only computed properties, as their single code block is implicitly a getter.
var diagonal: Int {
get {
let result = (height * height + width * width).squareRoot().rounded()
return Int(result)
}
// there’s no return statement in a setter — it only modifies the other stored properties.
set {
// provide a reasonable default value for the screen ratio
let ratioWidth = 16.0
let ratioHeight = 9.0
let ratioDiagonal = (ratioWidth * ratioWidth + ratioHeight * ratioHeight).squareRoot()
// The newValue constant lets you use whatever value was passed in during the assignment.b”, the newValue is an Int, so to use it in a calculation with a Double, you must first convert it to a Double.c.
height = Double(newValue) * ratioHeight / ratioDiagonal
width = height * ratioWidth / ratioHeight
}
}
}
Type properties
A type property is declared with the modifier static.
Using a type property means you can retrieve the same stored property value from anywhere in the code for your app or algorithm
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
}
let highestLevel = Level.highestLevel
Property observers
A willSet observer is called when a property is about to be changed while a didSet observer is called after a property has been changed.
willSet and didSet observers are only available for stored properties. If you want to listen for changes to a computed property, simply add the relevant code to the property’s setter.
the willSet and didSet observers are not called when a property is set during initialization
struct Level {
static var highestLevel = 1
let id: Int
var boss: String
var unlocked: Bool {
didSet {
// You are required to use the full name Level.highestLevel rather than just highestLevel alone to indicate you’re accessing a type property
if unlocked && id > Level.highestLevel {
Level.highestLevel = id
}
}
}
}
Limiting a variable
use property observers to limit the value of a variable
struct LightBulb {
static let maxCurrent = 40
var current = 0 {
didSet {
if current > LightBulb.maxCurrent {
print("""
Current is too high,
falling back to previous setting.
""")
current = oldValue
}
}
}
}
var light = LightBulb()
light.current = 50
var current = light.current // 0
light.current = 40
current = light.current // 40
Lazy properties
If you have a property that might take some time to calculate, you don’t want to slow things down until you actually need the property. Say hello to the lazy stored property
struct Circle {
// The lazy property must be a variable, defined with var, instead of a constant defined with let
lazy var pi = {
return ((4.0 * atan(1.0 / 5.0)) - atan(1.0 / 239.0)) * 4.0
}()
var radius = 0.0
var circumference: Double {
// the circumference getter must be marked as mutating. Accessing the value of pi changes the value of the structure.
mutating get {
return pi * radius * 2
}
}
// Since pi is a stored property of the structure, you need a custom initializer to use only the radius. Remember the automatic initializer of a structure includes all of the stored properties.
init(radius: Double) {
self.radius = radius
}
}
var circle = Circle(radius: 5) // got a circle, pi has not been run
let circumference = circle.circumference // 31.42
// also, pi now has a value
Key points
Properties are variables and constants that are part of a named type.
Stored properties allocate memory to store a value.
Computed properties are calculated each time your code requests them and aren’t stored as a value in memory.
The static modifier marks a type property that’s universal to all instances of a particular type.
The lazy modifier prevents a value of a stored property from being calculated until your code uses it for the first time. You’ll want to use lazy initialization when a property’s initial value is computationally intensive or when you won’t know the initial value of a property until after you’ve initialized the object.
Last updated
Was this helpful?