This is the 10th day of my participation in the August More text Challenge. For details, see: August More Text Challenge
This article mainly introduces the following points
-
Understand object creation through SIL
-
Analysis of Swift class structure
-
Store properties & compute properties
-
Deferred storage properties & singleton creation mode
SIL
In the underlying process, OC code and SWift code passDifferent compilers
Compile and passLLVM
To generate the.o
Executable file, as shown below
-
OC is compiled to IR by the clang compiler (see iOS- Underlying Principles 31: LLVM compilation process & Clang plug-in development), and then generated into executable files.
-
In Swift, it is compiled into IR by the SwifTC compiler and then regenerated into executable files
Here is the compilation process in Swift, whereSIL
(Swift Intermediate Language) is in the Swift compilation processThe middle code
, mainly used for further analysis and optimization of Swift code. As shown in the figure below,SIL
Located in theAST
andLLVM
Between the IR
Note: The difference between Swift and OC is that Swift generates a high-level SIL
We can go throughswiftc -h
Terminal command to view all swiftc commands
For example, the following code is defined in the main.swift file
class CJLTeacher{
var age: Int = 18
var name: String = "CJL"
}
var t = CJLTeacher()
Copy the code
- View the abstract syntax tree:
swiftc -dump-ast main.swift
- Generate an SIL file:
swiftc -emit-sil main.swift >> ./main.sil && code main.sil
, where the entry function of main is as follows
// main // '@main' : identifies the 'entry function' of the current main.swift, the identifier name in SIL is prefixed with '@' sil@main: $@convention(c) (Int32, UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>) -> Int32 {// '%0, %1' = register, Once assigned, it is not modifiable. If you want to continue using it, you need to keep adding numbers (note: Bb0 (%0: $Int32, %1: $Int32) bb0(%0: $Int32, %1: $UnsafeMutablePointer<Optional<UnsafeMutablePointer<Int8>>>): / / ` alloc_global ` : create a ` ` global variables, namely the code ` t ` alloc_global @ $s4main1tAA10CJLTeacherCvp / / id: % 2 / / ` global_addr ` : access to the global variable addresses, and assigned to register % 3% 3 = global_addr @ $s4main1tAA10CJLTeacherCvp: $* CJLTeacher / / user: %4 %4 = metatype $@thick cjlteacher. Type // user: %5 = function_ref cjLteacher.__allocating_init () %5 = function_ref @$s4main10CJLTeacherCACycfC : $@convention(method) (@thick CJLTeacher.Type) -> @owned CJLTeacher // user: %6 // 'apply' calls' __allocating_init 'to initialize a variable and assign it to %6 %6 = apply %5(%4) : $@convention(method) (@thick CJLTeacher.Type) -> @owned CJLTeacher // user: %6 to %3: $*CJLTeacher // id: %6 to %3: $*CJLTeacher // id: %8 = integer_literal $builtin.int32, 0 // user: %9 %9 = struct $Int32 (%8: $Builtin.Int32) // user: %10 return %9 : $Int32 // id: %10 } // end sil function 'main'Copy the code
Note: the code command is configured in. ZSHRC. You can specify the software to open the corresponding file on the terminal
$open. ZSHRC / / * * * * * * add the following aliases alias subl = '/ Applications/SublimeText app/Contents/SharedSupport/bin/subl' alias Code = '/ Applications/Visual \ Studio \ code app/Contents/Resources/app/bin/code' / / * * * * * * use $code. The main sil / / if you want to highlight sil file, You need to install the plug-in VSCode SILCopy the code
- From the SIL file, it can be seen that the code has been obfuscated and can be restored with the following command to
s4main1tAA10CJLTeacherCvp
For example:xcrun swift-demangle s4main1tAA10CJLTeacherCvp
- Search in the SIL file
s4main10CJLTeacherCACycfC
, its internal implementation is mainlyAllocate memory + initialize variables
allocing_ref
: Create aCJLTeacher
The current reference count of the instance object is1
- call
init
methods
/ / * * * * * * * * * the main entrance to the function code in * * * * * * * * * % 5 = function_ref @ $s4main10CJLTeacherCACycfC: $@ convention (method) (@ thick CJLTeacher. Type) - > @ owned CJLTeacher / / s4main10CJLTeacherCACycfC is actual __allocating_init () // CJLTeacher.__allocating_init() sil hidden [exact_self_class] @$s4main10CJLTeacherCACycfC : $@convention(method) (@thick CJLTeacher.Type) -> @owned CJLTeacher { // %0 "$metatype" bb0(%0 : $@thick cjlteacher. Type): // allocate memory space on the stack %1 = alloc_ref $CJLTeacher // user: % 3 / / function_ref CJLTeacher. The init () to initialize the variable % 2 = function_ref @ $s4main10CJLTeacherCACycfc: $@convention(method) (@owned CJLTeacher) -> @owned CJLTeacher // user: %3 // return %3 = apply %2(%1) : $@convention(method) (@owned CJLTeacher) -> @owned CJLTeacher // user: %4 return %3 : $CJLTeacher // id: %4 } // end sil function '$s4main10CJLTeacherCACycfC'Copy the code
The SIL language is important for Swift source analysis, and more information about its syntax can be found on this website
Symbolic breakpoint debugging
- Set in Demo
_allocing_init
Symbol breakpoint
Found its internal call isswift_allocObject
The source code to debug
Let’s explore the creation of objects in Swift with swift_allocObject
- in
REPL
(Command interaction line, similar to Python, where you can write code) write (or copy) the following code and search for itswift_allocObject
Function to add a breakpoint and define an instance object t
- The breakpoint is broken. Check the details of local on the left
- Among them
requiredSize
Is the actual memory size allocated, which is 40 requiredAlignmentMask
Is the byte alignment in SWIFT, this is the same as in OC, must be8
Multiple of, the shortfall will be automatically filled, in order toSpace for time
To provide memory operation efficiency
Swift_allocObject source analysis
The source code for swift_allocObject is as follows
- through
swift_slowAlloc
Allocate memory and align memory bytes - through
new + HeapObject + metadata
Initializes an instance object - The return value of the function is
HeapObject
Type, so the current object’sMemory structure
isHeapObject
Memory structure of
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata, size_t requiredSize, size_t requiredAlignmentMask) { assert(isAlignmentMask(requiredAlignmentMask)); auto object = reinterpret_cast<HeapObject *>( swift_slowAlloc(requiredSize, requiredAlignmentMask)); // Allocate memory + byte alignment // NOTE: this relies on the C++17 guaranteed semantics of no null-pointer // check on the placement new allocator which we have observed on Windows, // Linux, and macOS. new (object) HeapObject(metadata); // If leak tracking is enabled, start tracking this object. SWIFT_LEAKS_START_TRACKING_OBJECT(object); SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject); return object; }Copy the code
- Enter the
swift_slowAlloc
Function, its interior is mainly bymalloc
inThe heap
The distribution ofSize Indicates the size of the memory
And,Return memory address
Is primarily used to store instance variables
void *swift::swift_slowAlloc(size_t size, size_t alignMask) { void *p; // This check also forces "default" alignment to use AlignedAlloc. if (alignMask <= MALLOC_ALIGN_MASK) { #if defined(__APPLE__) p = malloc_zone_malloc(DEFAULT_ZONE(), size); #else p = malloc(size); #endif} else {size_t alignment = (alignMask == ~(size_t(0)))? _swift_MinAllocationAlignment : alignMask + 1; p = AlignedAlloc(size, alignment); } if (! p) swift::crash("Could not allocate memory."); return p; }Copy the code
- Enter the
HeapObject
To initialize the method, take two parameters:The metadata, refCounts
–metadata
Type isHeapMetadata
Is a pointer type, accounting for8
Byte –refCounts
Reference count, type isInlineRefCounts
And theInlineRefCounts
Is a classRefCounts
The alias of, occupy8
In bytes), swift uses ARC reference counts
conclusion
-
For instance object T, it is essentially a HeapObject structure with a default memory size of 16 bytes (metadata 8 bytes + refCounts 8 bytes)
-
The OC instance object is essentially a structure inherited from the objc_object template. It has an 8 byte isa pointer
-
The default value of the instance object in Swift is one more reference count than the value of the refCounted in OC. The default value is 16 bytes
-
-
The memory allocation process for objects in Swift is as follows: __allocating_init –> swift_allocObject –> _swift_allocObject –> swift_slowAlloc –> malloc
-
Init’s job here is to initialize variables, as in OC
From the above analysis, we left two questions: what is metadata and how is 40 calculated? So let’s continue our exploration
In the demo, we can go throughRuntime
Method to get the memory size of the class
This is the same as the local requiredSize value on the left during source debugging. We know from our HeapObject analysis that a class with no properties has a default size of 16 bytes.
For Int, String, enter the underlying definition, both are structs, so are they both 8 bytes? You can verify this by printing out its memory size
********* @frozen public struct Int: FixedWidthInteger, SignedInteger {... } / / * * * * * * * * * the String to the underlying definition * * * * * * * * * @ frozen public struct String {... } / / * * * * * * * * * verify the * * * * * * * * * print (MemoryLayout < Int >. Stride) print (MemoryLayout < String >. Stride) / / * * * * * * * * * printing result * * * * * * * * * 8 to 16Copy the code
As you can see from the printed results, the Int type is 8 bytes and the String type is 16 bytes (more on this later). This is different from OC
So this explains why the memory size of CJLTeacher is equal to 40, that is, 40 = metadata (8 bytes) +refCount (8 bytes) + Int (8 bytes) + String (16 bytes)
The source of 40 is verified, but what metadata is still unknown
Explore the structure of classes in Swift
In OC, classes are inherited from the objc_class template. For details, see iOS- Underlying Principles 08: Class & Class Structure Analysis
In Swift, the class structure is HeapObject at the bottom, with metadata + refCounts
HeapMetadata type analysis
Let’s analyze metadata to see what it is.
- Enter the
HeapMetadata
Definition,TargetHeapMetaData
An alias for the type that takes a parameterInprocess
using HeapMetadata = TargetHeapMetaData<Inprocess>;
Copy the code
- Enter the
TargetHeapMetaData
Definition, its essence is aTemplate type
Which defines some of the required data structures. There are no properties in this structure, justInitialize the
Method, passed in aMetadataKind
Type (this structure does not have, so only in the superclass) herekind
That’s what’s passed inInprocess
// Template <typename Runtime> struct TargetHeapMetadata: TargetMetadata<Runtime> { using HeaderType = TargetHeapMetadataHeader<Runtime>; TargetHeapMetadata() = default; // Initialize method ConstExpr TargetHeapMetadata(MetadataKind kind) : TargetMetadata<Runtime>(kind) {} #if SWIFT_OBJC_INTEROP constexpr TargetHeapMetadata(TargetAnyClassMetadata<Runtime> *isa) : TargetMetadata<Runtime>(isa) {} #endif };Copy the code
- Enter the
TargetMetaData
Definition, there’s onekind
Properties,kind
Is the type passed in beforeInprocess
. And that tells us that forkind
, whose type isunsigned long
, which is used to distinguish the type of metadata
******** struct TargetMetaData{using StoredPointer = typename Runtime: StoredPointer; //******** TargetMetaData definition ******** struct TargetMetaData{using StoredPointer = typename Runtime: StoredPointer; . StoredPointer kind; } / / * * * * * * * * Inprocess definition * * * * * * * * struct Inprocess {... using StoredPointer = uintptr_t; . } / / * * * * * * * * uintptr_t definition * * * * * * * * typedef unsigned long uintptr_t.Copy the code
From the definition of TargetHeapMetadata and TargetMetaData, we can see that the type of kind parameter in the initialization method is MetadataKind
- Enter the
MetadataKind
Definition, there’s one in there#include "MetadataKind.def"
, click to enter, which recordsAll types of metadata
, sokind
The categories are summarized as follows
name | value |
---|---|
Class | 0x0 |
Struct | 0x200 |
Enum | 0x201 |
Optional | 0x202 |
ForeignClass | 0x203 |
Opaque | 0x300 |
Tuple | 0x301 |
Function | 0x302 |
Existential | 0x303 |
Metatype | 0x304 |
ObjCClassWrapper | 0x305 |
ExistentialMetatype | 0x306 |
HeapLocalVariable | 0x400 |
HeapGenericLocalVariable | 0x500 |
ErrorObject | 0x501 |
LastEnumerated | 0x7FF |
- Go back to
TargetMetaData
Structure definition, find the methodgetClassObject
In this methodMatch the kind
The return value isTargetClassMetadata
type- If it is
Class
, this (the current pointer, that is, metadata) is forcibly convertedClassMetadata
- If it is
const TargetClassMetadata<Runtime> *getClassObject() const; //******** specific implementation ******** template<> inline const ClassMetadata * Metadata::getClassObject() const {// Matches kind Switch (getKind()) {// If kind is a class case MetadataKind:: class: {// Native Swift class metadata is also the class object. // Convert the current pointer to ClassMetadata. Return static_cast<const ClassMetadata *>(this); } case MetadataKind::ObjCClassWrapper: { // Objective-C class objects are referenced by their Swift metadata wrapper. auto wrapper = static_cast<const ObjCClassWrapperMetadata *>(this); return wrapper->Class; } // Other kinds of types don't have class objects. default: return nullptr; }}Copy the code
We can verify this with LLDB
po metadata->getKind()
, we get its kind is Classpo metadata->getClassObject()
X /8g 0x0000000110EFDC70, this address stores metadata information!
So, TargetMetadata is essentially the same as TargetClassMetadata, because we can convert Pointers directly in the memory structure, so we can say that the structure is actually TargetClassMetadata
- Enter the
TargetClassMetadata
Definition, inherited fromTargetAnyClassMetadata
, has the following properties, which are part of the class structure
template <typename Runtime> struct TargetClassMetadata : public TargetAnyClassMetadata<Runtime> { ... // ClassFlags Flags; // Uint32_t InstanceSize; // Uint16_t InstanceAlignMask; // Uint16_t Reserved at runtime; // Uint32_t ClassSize; Uint32_t ClassAddressPoint; // Uint32_t ClassAddressPoint; . }Copy the code
- Enter the
TargetAnyClassMetadata
Definition, inherited fromTargetHeapMetadata
template <typename Runtime> struct TargetAnyClassMetadata : public TargetHeapMetadata<Runtime> { ... ConstTargetMetadataPointer<Runtime, swift::TargetClassMetadata> Superclass; TargetPointer<Runtime, void> CacheData[2]; StoredSize Data; . }Copy the code
conclusion
To sum up, whenmetadata
thekind
For Class, there is the following inheritance chain:
- The actual type returned by the current class is
TargetClassMetadata
There is only one property in TargetMetaDatakind
.TargetAnyClassMetaData
There are three properties in theKind, superclass, cacheData
- The current
The in-memory properties of the Class
byTargetClassMetadata
Attribute +TargetAnyClassMetaData
Attribute +TargetMetaData
Property, so the resulting metadata data structure is shown below
struct swift_class_t: NSObject{ void *kind; // Equivalent to isa in OC, the actual type of kind is unsigned long void *superClass; void *cacheData; void *data; uint32_t flags; // uint32_t instanceAddressOffset; // uint32_t instanceSize; // uint32_t instanceSize; // 3 byte uint16_t instanceAlignMask; // uint16_t reserved; // uint32_t classSize; // uint32_t classSize; // uint32_t classAddressOffset; // uint32_t classAddressOffset; //4 bytes void *description; . }Copy the code
Compared with OC
- Instance object & class
-
Instance objects in OC are essentially structures, created from the underlying objc_object template, and classes are inherited from objc_class
-
The instance object in Swift is essentially a structure with type HeapObject and one more refCounts than OC
-
- Methods list
-
Methods in OC are stored in the methodList of the objc_class structure class_rw_t
-
Methods in Swift are stored in the metadata metadata
-
- Reference counting
-
ARC in OC maintains hash tables
-
ARC in Swift has a refCounts attribute inside the object
-
Swift properties
In SWIFT, attributes are divided into the following categories
-
Storage properties
-
Calculate attribute
-
Delayed storage property
-
The type attribute
Storage properties
There are two types of storage properties:
-
Or a constant storage property, the let modifier
-
Or the variable stores the property, the var decoration
Define the following code
class CJLTeacher{
var age: Int = 18
var name: String = "CJL"
}
let t = CJLTeacher()
Copy the code
The age and name in the code are both variable storage attributes, which can be reflected in SIL
Class CJLTeacher {// @_hasstorage @_hasinitialValue var age: Int { get set } @_hasStorage @_hasInitialValue var name: String { get set } @objc deinit init() }Copy the code
Storage property: occupies the memory space of the allocated instance object
Let’s verify this with breakpoint debugging
-
po t
-
X / 8G memory address, that is, the HeapObject storage address
The memory structure is as follows
Calculate attribute
Calculated properties: Properties of set/ GET methods that do not occupy memory space
Let’s do a demo to show you, is this correct?
class CJLTeacher{
var age: Int{
get{
return 18
}
set{
age = newValue
}
}
}
Copy the code
In real programming, the compiler will report the following warning, which meansIn the set method of age, we call age.set again
And then the run discovery crashes becauseCall age.set in the set method of age
Led to theCircular references, or recursion
To verify that it does not occupy memory space, we can use the following example to verify that it does not occupy memory space, print the memory size of the following class
Class Square{var width: Double = 8.0 var area: Double{get{Double Return width * width} set{width = SQRT (newValue)}}} print(class_getInstanceSize(square.self)) //********* Print the result ********* 24Copy the code
(metadata + refCounts) = 16 bytes + width (8 bytes) = 24; From here we can prove that the area attribute does not occupy memory space.
Validation: The essence is set/get methods
- Convert main.swift to an SIL file:
swiftc -emit-sil main.swift >> ./main.sil
- View the SIL file, for
Storage properties
, there are_hasStorage
The identifier
class Square {
@_hasStorage @_hasInitialValue var width: Double { get set }
var area: Double { get set }
@objc deinit
init()
}
Copy the code
- For computed attributes,
SIL
Only in the sEtter, getter
methods
Attribute Observer (didSet, willSet)
-
WillSet: Call newValue before new values are stored
-
DidSet: oldValue is called after the new value is stored
validation
- You can use demo to verify this
class CJLTeacher{ var name: String = "test "{// call willSet{print("willSet newValue \(newValue)")}} // call didSet{print("didSet oldValue ") after the newValue is stored \ [oldValue) ")}}} t = CJLTeacher var () t.n ame = "CJL" / / * * * * * * * * * * printing result * * * * * * * * * willSet newValue CJL didSet oldValue testCopy the code
- You can also verify this by compiling main.swift to mail.sil and looking in the sil file
name
theset
methods
Question 1: Does the init method trigger a property observer? In the following code, does setting name in the init method trigger the property observer?
class CJLTeacher{ var name: String = "test "{// call willSet{print("willSet newValue \(newValue)")}} // call didSet{print("didSet oldValue ") after the newValue is stored \(oldValue)") } } init() { self.name = "CJL" } }Copy the code
WillSet () didSet () didSet () didSet () didSet ()
- in
init
Method, yes if the attribute is calledNot trigger
Attribute observer - The main thing in init is
Initializes the current variable
, other than the default first 16 bytesmemset
Clean up the memory space (because it might be dirty, that is, used by someone else) before assigning a value
[Summary] : Initializers (that is, init method Settings) and default values set at definition (that is, calls to other property values in didSet) are not triggered
Question 2: Where can I add a property observer?
There are mainly three places to add:
- 1.
class
Defined in theStorage properties
- 2. By class
Inherited storage properties
class CJLMediumTeacher: CJLTeacher{ override var age: Int{// willSet{print("willSet newValue \(newValue)")} // didSet{print("didSet oldValue \(oldValue)")} }}}Copy the code
- 3. Pass the class
Inherited computed properties
class CJLTeacher{ var age: Int = 18 var age2: Int { get{ return age } set{ self.age = newValue } } } var t = CJLTeacher() class CJLMediumTeacher: CJLTeacher{ override var age: Int{// willSet{print("willSet newValue \(newValue)")} // didSet{print("didSet oldValue \(oldValue)")} } } override var age2: Int{// willSet{print("willSet newValue \(newValue)")} // didSet{print("didSet oldValue \(oldValue)")} }}}Copy the code
Question 3: What is the order in which the computational properties of both the subclass and the parent class are called with didset and willset?
What is the order in which the following code is called?
class CJLTeacher{ var age: Int = 18{// call willSet{print(" superclass willSet newValue \(newValue)")} // Call didSet{print(" superclass didSet oldValue ") \(oldValue)") } } var age2: Int { get{ return age } set{ self.age = newValue } } } class CJLMediumTeacher: CJLTeacher{ override var age: Int{// call willSet{print(" subclass didSet oldValue \(oldValue)")} } } var t = CJLMediumTeacher() t.age = 20Copy the code
The running results are as follows:
Conclusion: For the same property, both the subclass and the parent class have property observers in the order: first the subclass willset, then the parent willset, in the parent didset, the subclass didset, namely: the child parent and the child
Question 4: If a subclass calls init of its parent class, does the observation property trigger?
On the basis of question 3, modify the CJLMediumTeacher class
class CJLMediumTeacher: CJLTeacher{ override var age: Int{// call willSet{print(" subclass willSet newValue \(newValue)")} // Call didSet{print(" subclass didSet oldValue ") \(oldValue)")}} Override init() {super.init() self.age = 20}} //****** Print the result ****** subclass willSet newValue 20 superclass WillSet newValue 20 Parent didSet oldValue 18 subclass didSet oldValue 18Copy the code
From the printed results, we can see that the property observer is triggered, mainly because the subclass calls the parent init, which has already been initialized, and the initialization process guarantees that all properties have values (i.e., super.init ensures that variables are initialized), so the property can be observed
Lazy properties
The delay attribute is mainly explained as follows:
-
1. Storage properties decorated with lazy
-
2. The delay attribute must have a default initial value
-
3. Deferred storage is not assigned until the first access
-
Delayed storage of properties does not guarantee thread-safety
-
5. The effect of delayed storage properties on the size of instance objects
Let’s analyze it one by one
1. Storage properties decorated with lazy
class CJLTeacher{
lazy var age: Int = 18
}
Copy the code
2. The delay attribute must have a default initial value
If the type is defined as optional, an error is reported, as shown below
3. Delayed storage is not assigned until the first access. You can debug the instance variable to see the memory change
- age
Before the first visit
The age isNo value
for0x0
- age
After the first visit
In this case age isHave a value
For 30
Thus, you can verify that lazy load storage properties are assigned only on the first access
We can also look at the sil file, where we can generate the sil file by adding the obfuscated swift command (i.e., xcrun swift-demangle) : Swiftc – emit – sil main. Swift | xcrun swift – demangle > >. / main sil && code. The main sil, demo code is as follows
class CJLTeacher{
lazy var age: Int = 18
}
var t = CJLTeacher()
t.age = 30
Copy the code
Kind of + main
The: lazy storage property is an underlying oneoptional
type
setter+getter
: You can verify from the getter method that on the first access, a value is changed from a non-value to a valued operation
Through SIL, the following two points are stated:
-
The lazy property, which defaults to optional at the bottom level, defaults to nil when not accessed, and is 0x0 in memory. On the first access, the getter method of the property is called, and its internal implementation is to perform an assignment through a branch of the current enum
-
2. Is the optional type 16 bytes? You can print through MemoryLayout
- Size: actual size
- Stride: Allocate size (mainly due to memory alignment)
Print (MemoryLayout < Optional < Int > >. The stride) print (MemoryLayout < Optional < Int > >. The size) / / * * * * * * * * * * * printing result * * * * * * * * * * * 16 SeptemberCopy the code
Why is the actual size 9? Optional is an enum in which the Int is 8 bytes and the other byte is used to store the case value (more on this later).
Delayed storage of properties does not guarantee thread-safety
Look at the getters for age, if there are two threads:
-
Thread 1 accesses age, which has no value, and enters the BB2 process
-
The timeslice then allocates the CPU to thread 2, which for Optional is still None and can also go to the BB2 process
-
So, at this point, thread 1 will go through the assignment, thread 2 will go through the assignment, and there is no guarantee that the property is only initialized once
5. The effect of lazy storage on the size of an instance object
Do not use the lazy
In the case of modification,class
The memory size of is24
The use of lazy
In this case, the memory size of the class is32
Thus, we can prove that using lazy and not using lazy, the memory size of the example object is different
The type attribute
Type attribute, mainly have the following points:
-
1. Use the keyword static and it is a global variable
-
The type attribute must have a default initial value
-
3. Type attributes are initialized only once
1. Use the keyword static
Class CJLTeacher{static var age: Int = 18} // **** use **** var age = CJLTeacherCopy the code
Generating SIL files
- Looking at the definition, there is one more
The global variable
A type attribute is a global variable
- Look at the entry function for obtaining age
- Look at the getter method for age
–globalinit_029_12232F587A4C5CD8B1EEDF696793B2FC_func0
Is a global variable initialization function – builtin "once"
, through breakpoint debugging, found that the call isswift_once
, indicating that the property is initialized only once
- Search in source code
swift_once
Its interior is throughGCD
theDispatch_once_f singleton
The implementation. This is where point 3 above can be verified
void swift::swift_once(swift_once_t *predicate, void (*fn)(void *),
void *context) {
#if defined(__APPLE__)
dispatch_once_f(predicate, context, fn);
#elif defined(__CYGWIN__)
_swift_once_f(predicate, context, fn);
#else
std::call_once(*predicate, [fn, context]() { fn(context); });
#endif
}
Copy the code
The type attribute must have a default initial value
As shown in the following figure, an error will be reported if the default initial value is not given
So for a type attribute, one is global and only initialized once, and the other is thread-safe
The creation of singletons
//****** Swift singleinstance ****** class CJLTeacher{//1, static let shareInstance = cjlteacher.init () Private init(){}} Var t = cjlteacher. shareInstance //****** OC singleton ****** @implementation CJLTeacher + (instancetype)shareInstance{ static CJLTeacher *shareInstance = nil; dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ shareInstance = [[CJLTeacher alloc] init]; }); return shareInstance; } @endCopy the code
conclusion
-
Storing properties takes up the memory space of instance variables, and
-
Evaluated properties do not take up memory space and are essentially set/get methods
-
Attribute observer
-
Willset: called before the new value is stored, to notify the subclass first and then the parent (because some additional operations may need to be done in the parent), namely the child parent
-
DidSet: After the new value is stored, it is told to the parent class first and then to the child class (the parent class takes precedence over the child class), that is, the parent and child
-
An init method assignment in a class does not trigger property observation
-
Properties can be added to a class-defined storage property, inherited storage property, or inherited computing property
-
When a subclass calls the init method of its parent class, the observation property is triggered
-
-
Delayed storage property
-
The storage property is decorated with lazy and must have a default value
-
It is assigned only the first time it is accessed and is thread unsafe
-
The use of lazy and non-lazy affects the memory size of the instance object, mainly because the type of lazy is optional. The nature of the optional is enum. In addition to the memory size of the property itself, a byte is required to store the case
-
-
The type attribute
-
Use static decoration and must have a default initial value
-
Is a global variable that is only initialized once and is thread safe
-
To create a singleton object:
-
Create an instance variable using static + let
-
The init method has private access
-
-