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?