First, storage properties
A storage property is a constant or variable that is part of an instance of a particular class and structure. A store property is either a variable store property (introduced by the var keyword) or a constant store property (introduced by the let keyword). There’s nothing special about storage properties, because they’re everywhere, right
class LGTeacher{
var age: Int
var name: String
}
Copy the code
Let is used to declare constants. The value of a constant cannot be changed once it is set. Var is used to declare variables whose values can be set to different values in the future.
1 let 和 varThe difference between:
var age = 18
let x = 20
Copy the code
1.1 assemblyAngle analysis of
Is whether or notlet
orvar
In terms of compilation, essentiallylet
andvar
They’re addresses. There’s no difference between the two
1.2 SILAngle analysis of
@_hasStorage @_hasInitialValue var age: Int { get set }
@_hasStorage @_hasInitialValue let x: Int { get }
Copy the code
Var x: Only get methods
They’re all store properties, they all have an initial value and the default store properties are all pretty composite get/set methods store properties access is GET, assignment is set, let doesn’t generate set methods; When used, the let modifier will not change the value, and the var modifier will change the value.
Ii. Calculating attributes (Computed Property
)
Stored properties are the most common. In addition to storing properties, classes, structures, and enumerations can also define computed properties. Computed properties do not store values; they provide getters and setters to modify and retrieve values. A stored property can be a constant or variable, but a calculated property must be defined as a variable. At the same time we must include the type when we write the computed property, because the compiler needs to know what the expected return value is.
Struct square{struct square; struct square; struct square; Double { get{ return width * width } set{ self.width = newValue } } }Copy the code
2.1 read-onlyCalculate attribute
There is no set, only get
- A model:
struct square{
var width: Double = 30
var area: Double {
get{
return width * width
}
}
}
Copy the code
Area does not assign to either internal or external values, but only reads data
- Model 2:
struct square{
var width: Double = 30
private(set) var area: Double = 40
func test() {
self.area
}
}
Copy the code
SIL analysis
struct square {
@_hasStorage @_hasInitialValue var width: Double { get set }
@_hasStorage @_hasInitialValue private(set) var area: Double { get set }
func test()
init()
init(width: Double = 30, area: Double = 40)
}
Copy the code
Externally, there is only the get attribute, and no assignment; It can be read and assigned internally
Attribute observer
3.1 the observerwillSet && didSet
The property observer will look for changes to the property value, a willSet is called when the property is about to change, even if the value is the same as the original value, whereas didSet is called after the property has changed. Their syntax is similar to getters and setters.
class SubjectName {
var subjectName: String = "" {
willSet {
print("subjectName will set value \(newValue)")
}
didSet{
print("subjectName has been chaged \(oldValue)")
}
}
}
let s = SubjectName()
s.subjectName = "Swift"
Copy the code
SIL analysis
3.2 The observerInitialization operation
One thing to note here when we use the property viewer is that the willSet and didSet observers are not called when the property is set during initialization; They are called only when a new value is assigned to a fully initialized instance. Run the following code and you will see that there is no output at the moment.
class SubjectName { var subjectName: String = "" { willSet { print("subjectName will set value \(newValue)") } didSet{ print("subjectName has been chaged \(oldValue)") } } init(subjectName: String) {// Initialize operation self.subjectName = subjectName}} Let s = subjectName (subjectName: "Swift advanced ")Copy the code
SIL analysis
Class to copy the value directly from memory to the property
3.3 The observer -> Calculate attribute
Just add the relevant code to the setter for the property without writing the observe property, as shown in the following example:
3.4 The observer– > inheritance
I’m going to write an LGTeacher class, and then I’m going to inherit LGTeacher, and I’m going to change the value of the inherited class
class LGTeacher { var age: Int { willSet{ print("age will set value \(newValue)") } didSet{ print("age has been changed \(oldValue)") } } var height: Double init(_ age : Int, _ height: Double) { self.age = age self.height = height } } class LGParTimeTeacher: LGTeacher { override var age: Int { willSet{ print("override age will set value \(newValue)") } didSet{ print("override age has been changed \(oldValue)") } } var subjectName: String init(_ subjectName: String) {self.subjectName = subjectName super.init(18, 180.0) self.age = 20}} let t = LGParTimeTeacher("Swift")Copy the code
Execution Result:
override age will set value 20
age will set value 20
age has been changed 18
override age has been changed 18
Program ended with exit code: 0
Copy the code
It can be concluded that the call order is:
Override willSet -> willSet -> didSet -> override didSet
SIL analysis:
4. Deferred storage properties
- The initial value of a deferred storage attribute is not evaluated until it is first used.
- With keywords
lazy
To identify a deferred storage property
class Subject {
lazy var age: Int = 18
}
var s = Subject()
print(s.age)
print("end")
Copy the code
LLDB
Debugging analysis:
As shown in the figure above: Unused Used changed from 0x0000000000000000 to 0x0000000000000012, so the calculation is performed when it is used for the first time.
SIL
Analysis:
Lazy is initialized, and it is optional, not nil by default; Final modifier, cannot be overridden.
Observe the implementation of subject.age.getter
First access to __lazy_storage_ address and memory address value to register %4 enumerated pattern matching, have value go bb1 code block, no value go BB2 code block
bb2
Module, no value before the address assignment operation
Bb1 module, directly return the original value to out
Lazy is an optional value that is assigned to the attribute the first time it is used
5. Type attributes
5.1 Type Attributes
- A type attribute is really just a global variable
- Type attributes are initialized only once
class LGTeacher{
static var age: Int = 18
}
LGTeacher.age = 30
Copy the code
SIL analysis
// one-time initialization token for age
sil_global private @$s4main9LGTeacherC3age_Wz : $Builtin.Word
// static LGTeacher.age
sil_global hidden @$s4main9LGTeacherC3ageSivpZ : $Int
Copy the code
Age becomes a global variable, the global declares age; Static is essentially a global variable
5.2 Singleton Mode
class LGTeacher{
static let sharedInstance = LGTeacher()
private init(){}
}
Copy the code
Initialize privatized, externally accessible only through sharedInstance
The location of attributes in Mahco files
6.1 Information structure analysis of attributes
In the first article, we reviewed the Metadata structure:
struct Metadata{
var kind: Int
var superClass: Any.Type
var cacheData: (Int, Int)
var data: Int
var classFlags: Int32
var instanceAddressPoint: UInt32
var instanceSize: UInt32
var instanceAlignmentMask: UInt16
var reserved: UInt16
var classSize: UInt32
var classAddressPoint: UInt32
var typeDescriptor: UnsafeMutableRawPointer
var iVarDestroyer: UnsafeRawPointer
}
Copy the code
In the last article, you learned about Type Descriptors during method scheduling, which recorded information about V-tables. Now you need to learn about field Descriptors in Type Descriptors
struct TargetClassDescriptor{
var flags: UInt32
var parent: UInt32
var name: Int32
var accessFunctionPointer: Int32
var fieldDescriptor: Int32
var superClassType: Int32
var metadataNegativeSizeInWords: UInt32
var metadataPositiveSizeInWords: UInt32
var numImmediateMembers: UInt32
var numFields: UInt32
var fieldOffsetVectorOffset: UInt32
var Offset: UInt32
var size: UInt32
//V-Table
}
Copy the code
FieldDescriptor records the current property information, where the structure of the fieldDescriptor in the source code is as follows:
struct FieldDescriptor {
MangledTypeName int32
Superclass int32
Kind uint16
FieldRecordSize uint16
NumFields uint32
FieldRecords [FieldRecord]
}
Copy the code
NumFields represents how many attributes there are, and FieldRecords records information about each attribute. The structure of FieldRecords is as follows:
struct FieldRecord{
Flags uint32
MangledTypeName int32
FieldName int32
}
Copy the code
Flags: flag bit. MangledTypeName: Indicates the type of the current attribute. FieldName Attribute name
6.2 Finding attribute Information in a Mach-O file
Define an attribute structure and compile to generate a Mach-O file
class LGTeacher {
var age = 18
var age1 = 20
}
Copy the code
How do I find a Mach-O file?
- First display the project Products directory
- withMachOViewOpen the
Mach-o
Executable file.
Find the addresses of Descriptor in the Mach-o file, add them, subtract 0x100000000(the base address of virtual memory), and get a new address: 0x3D74
0x3EFC + 0xFFFFFE78 = 0x100003D74
0x100003D74 - 0x100000000 = 0x3D74
Copy the code
0x3D74
The location of the file in Mach-o is as follows
0x3D74 In the Mach-o file where the const address is 50 00 00 80, according to the TargetClassDescriptor class, find the fieldDescriptor by offsetting 4 member sizes (4 4-bytes).
50 01 00 00 is the offset information of a fieldDescriptor. To find the address of a fieldDescriptor, add the offset information. The calculation process is as follows (in small-endior-mode, read from right to left) :
0x3D80 + 0x4 + 0x0150 = 0x3ED4
Copy the code
According to the calculated result 0x3ED4, the location of the Mach-O file is as follows:
0x3ED4 is the start address of the FieldDescriptor. To find the address of the FieldRecords, offset the size of the member attribute before the FieldRecords. MangledTypeName 2 bytes, Superclass 2 bytes, Kind 2 bytes, FieldRecordSize 4 bytes, NumFields 4 bytes
FieldName address calculation process:
0x3EE4 + 0x4 + 0x4 + 0xFFFFDF = 0x100003ECB // Minus the base address of virtual memory 0x100003ECB-0x100000000 = 0x3ECBCopy the code
The calculated result is 0x3ECB, where Mach-o is as follows:
As shown, 0x3ECB is at reflStr in the Mach-o file
00, 61, 67, 65
: age31, 61, 67, 65
: age1