Thumbs up comments, feel useful friends can pay attention to the author public number iOS growth refers to north, continue to update
This is the 15th day of the Swift 100 Days series, which is a record of my Swift learning.
Structures and classes (collectively called “types”) can have their own variables and constants, which are collectively called properties.
Allows values to be attached to a type and uniquely represented.
We covered the use of attributes in the structures and Objects section.
Programming can not do without objects, objects can not do without attributes!
There are two types of properties in Swift: storage properties, which associate states with objects; Compute property, and perform a calculation based on that state.
Attribute viewer
When you declare a stored property, you can define a property observer using a closure that executes the code when the property is set. Swift allows you to observe properties that are about to change or have changed.
There are two types of property observers: willSet and didSet, which are called before or after a property change. In willSet Swift gives your code a special value called newValue to indicate the newValue that the property is going to change to, whereas in didSet you get oldValue to indicate the value before the update.
struct Product {
var price: Double {
willSet {
print("changing from \(price) to \(newValue)")}didSet {
print("changed from \(oldValue) to \(price)")}}}var product = Product(price: 30.0)
product.price = 28.0
product.price = 35.0
//changing from 30.0 to 28.0
// Changed from 30.0 to 28.0
//changing from 28.0 to 35.0
// Changed from 28.0 to 35.0
Copy the code
No function can be separated from practice, and functions without practical significance may not be urgent to wait for you to learn.
Added value constraint
Use the attribute viewer to add additional constraints on the values accepted by the type.
We add a minimum/high price to our product. When the value is less than/greater than the lowest/high value, it is reset to the lowest/high value.
struct Product {
var price: Double {
willSet {
print("changing from \(price) to \(newValue)")}didSet {
print("changed from \(oldValue) to \(price)")
if self.price < 30.0 {
self.price = 30.0}}}}var product = Product(price: 30.0)
product.price = 28.0
product.price = 35.0
//changing from 30.0 to 28.0
// Changed from 30.0 to 28.0
//changing from 30.0 to 35.0
// Changed from 30.0 to 35.0
Copy the code
Setting properties inside the observer does not trigger additional callbacks, and the code above does not create an infinite loop.
We don’t use the willSet observer because even if we do any assignment in its callback, it will be overridden when the property is given newValue.
Of course, the assignment was eventually executed. It just assigns the value that we want. If we want to specify a mechanism that reports an error if it is set incorrectly. You can’t do this with the didSet observer.
Avoid unnecessary refreshes
Use the property viewer to avoid unwanted refreshes. Or we need to update data when we need to.
If our UI is going to change because of some value. We should try to update the page when and only when the value changes, rather than restricting the assignment of variables.
what new
In Swift 5.3, attribute observers can be attached to lazy attributes, which allows us to automatically see when a new value has been assigned to a given attribute, even if its value was lazy-loaded when it was first accessed.
conclusion
Property observation using willSet and didSet provides a very powerful way to easily observe changes in values without any other type of abstraction. They can be used to ensure that we drive logic based on a single source of facts and allow us to update other values and states in a reactive manner when a given property changes.
Calculate attribute
A calculated property is a property that evaluates and returns a value, not just stores it.
Instead of storing values directly, computed properties provide a getter and an optional setter to indirectly obtain and set the values of other properties or variables.
Compute getters and setters for properties
A brief description of the syntax we use:
var property:type {
get {
code
}
set(value) {
code
}
}
Copy the code
A calculated property is a variable first, if a constant, there is no need to treat it as a calculated property.
Second, a value in a set method can be declared either explicitly or implicitly
set {
// Use 'newValue' in here (implicit)
}
set(newString) {
// Use 'newString' in here
}
Copy the code
Read-only computed property
A computed property that has only a getter and no setter is called a read-only computed property. A read-only calculation property always returns a value that can be accessed through the dot operator, but cannot set a new value.
var sellPrice: Double {
get {
return Double(count) * price
}
}
var description: String {
return "IOS growth is north"
}
var name: String {
"IOS growth is north"
}
Copy the code
Since Swift 5.1, single-line expressions can omit explicit returns for clarity, brevity, and expression.
The same effect can be achieved by using the return value of the function. In Swift, when choosing between computed properties and methods, I would choose to use read-only computed properties when:
- The API I’ve defined returns some form of information about the state of an object or value.
- The time complexity of its calculated attribute is
O(1)
— There is no need for overly complicated logical calculations
let sellPrice = product.sellPrice()
//or
let sellPrice = product.sellPrice
Copy the code
Lazy/lazy loading computed properties
Lazy attributes can make your iOS development more efficient and easier to read.
In the Day9 structure and object, we showed how to store attributes in Swift using lazy processing. However, simply using the lazy keyword to modify the calculated properties does nothing
class Circle {
var radius:Double = 0
lazy var circumference:Double {
Double.pi * 2 * radius
}
}
Copy the code
As we excitedly wrote the above code, the compiler reported an error
// Output: 'lazy' may not be used on a computed property
// Output: Lazy properties must have an initializer
Copy the code
So how do we implement a lazy/lazy-loaded computed property
We can do it this way.
class Circle {
var radius:Double = 0
lazy var circumference:Double = {
Double.pi * 2 * self.radius
}()
}
Copy the code
Lazy/lazy-loaded calculation properties now use closures for lazy calculation.
Lazy/lazy-loaded computed properties are computed only once!
You need to always be aware of when you need to use this method.
It is generally recommended as a syntactic sugar for code level UI drawing
lazy var label:UILabel = {
UILabel.init() ()}Copy the code
Lazy loading delays the initialization of a property until it is first accessed. Computed attributes do not allow the lazy keyword, so implicitly invoked closures can be used to achieve the same effect.
Property Wrappers
At WWDC 2019, with the introduction of SwiftUI, we saw the property wrapper for the first time in Swift.
Since Swift 5.1, the Swift family has had a series of @s.
In addition to being useful in SwiftUI, attribute wrappers are also useful in real Swift programming.
Before we look at SwiftUI, let’s take a concrete look at the property wrapper.
What is a property wrapper?
Attribute wrappers in Swift allow you to extract common logic in different wrapper objects.
A property wrapper can be thought of as an additional layer that defines how properties are stored or evaluated when read. It is particularly useful for replacing duplicate code in getters and setters for properties.
Transparently Wrapping
We define a property wrapper that implements uppercase names
@propertyWrapper struct Capitalized {
var wrappedValue: String {
didSet {
wrappedValue = wrappedValue.capitalized
}
}
init(wrappedValue: String) {
self.wrappedValue = wrappedValue.capitalized
}
}
Copy the code
Note: We need to explicitly capitalize any strings passed into the initializer, because the attribute observer is triggered only after the value or object is fully initialized.
We define two constructs, one User and one Book. And both structures capitalize the first letter of the name attribute.
Instead of using a property wrapper, we can use a property observer to reassign in didSet.
But we can use it this way
struct User {
@Capitalized var name: String
}
let user = User(name: "IOS growth is north")
struct Book {
@Capitalized var name: String = "IOS growth is north"
}
let book = Book(a)print(user.name)
print(book.name)
//Ios growth refers to north
//Ios growth refers to north
Copy the code
So, when we need our property to have this effect, we just define the property with a @capitalized prefix.
Property wrapper and UserDefaults
Property wrappers can also have their own properties, allowing for further customization and even injecting dependencies into our wrapper types.
Swift has a storage feature called UserDefaults, an interface to a user’s default database that you can persist key-value pairs during application startup.
let defaults = UserDefaults.standard
defaults.set(25, forKey: "Age")
defaults.set("IOS growth is north", forKey: "name")
let age = defaults.integer(forKey: "Age") / / 25
let name = defaults.string(forKey: "name") //iOS growth refers to north
Copy the code
Doing so usually requires writing some form of mapping code to synchronize each value with its underlying UserDefaults store — usually this requires copying for each segment of data we want to store. And different fetch methods are used depending on the type.
However, we can make our method more elegant by using property wrappers
@propertyWrapper struct UserDefaultsBacked<Value> {
let key: String
var storage: UserDefaults = .standard
var wrappedValue: Value? {
get { storage.value(forKey: key) as? Value }
set { storage.setValue(newValue, forKey: key) }
}
}
Copy the code
When we need to use, we can store the data for need
@UserDefaultsBacked<String>(key: "name") var name
@UserDefaultsBacked<Int>(key: "age") var age
Copy the code
This way, when we assign a value, it will store the current object, and it will be very convenient to read the value, and we can get it directly with the syntax.
For now, this is enough to write clean enough code.
Thank you for reading this! 🚀