“This is the 20th day of my participation in the First Challenge 2022. For details: First Challenge 2022”

The type attribute

The attributes we mentioned earlier are actually instance attributes, and in Swift there are also type attributes, also known as class attributes. Instance attributes are called by the instance of the class, while type attributes are called directly by the class. Type attributes are declared using either the static or class keyword. Properties declared using the static keyword are also called static properties; The class keyword is used to declare computed attributes of classes that allow subclasses to overwrite their computed methods.

  • A type attribute is really just a global variable
  • Type attributes are initialized only once

Type attribute analysis

So how do you declare a type attribute? We add the static keyword to a normal age attribute and it becomes a type attribute:

class Person {
    static var age: Int = 18
}
Copy the code

When we access a type attribute, we can access it directly using the class name:

So how do type attributes differ from our normal attributes? We can analyze type attributes using SIL files:

From the SIL file, we can see that the type property is still a storage property, but with a static instead of a normal storage property, and the age property becomes a global variable; By implementing the main function:

According to comments, we see a Person. Age. UnsafeMutableAddressor is on a visit to the age attribute memory address, at this time is a function called s4main6PersonC3ageSivau, we locate to the realization of the function:

S4main6PersonC3age_WZ (); s4main6PersonC3age_WZ ();

A structure of type Int is added to memory (stored in the global variable age) after initialization.

The use of GCD in type attributes

How does s4main6PersonC3age_WZ ensure that it is called only once when the memory address of the age attribute is retrieved from the SIL file above? Let’s demote the code to IR code to see the implementation of this function:

Used in IR code to s4main6PersonC3ageSivau, according to the command line, we can see that it is a Person. Age. UnsafeMutableAddressor:

Swift_once is called by swift_once. Swift_once is called by swift_once.

In this function, dispatch_once_t from GCD is finally called to ensure that the global age attribute is initialized only once;

Implementation of a singleton class

So, we can implement the singleton class in Swift by combining static and let:

Of course this is not perfect, we need to privatize the specified initializer to ensure that it can only be accessed externally via shared:

The position of the property in MachO

As we have previously analyzed, the Swift Metadata data structure is as follows:

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

The typeDescriptor is stored in __swift5_types in MachO, and its data structure is as follows:

struct TargetClassDescriptor {
    ContextDescriptorFlags Flags;
    TargetRelativeContextPointer<Runtime> Parent;
    TargetRelativeDirectPointer<Runtime, const char, /*nullable*/ false> Name;
    TargetRelativeDirectPointer<Runtime.MetadataResponse(...). ./*Nullable*/ true> AccessFunctionPtr;
    TargetRelativeDirectPointer<Runtime, const reflection::FieldDescriptor./*nullable*/ true> Fields;
    TargetRelativeDirectPointer<Runtime, const char> SuperclassType;
    uint32_t MetadataNegativeSizeInWords;
    uint32_t MetadataPositiveSizeInWords;
    uint32_t NumImmediateMembers;
    uint32_t NumFields;
    uint32_t FieldOffsetVectorOffset;
}
Copy the code

And in that data structure, Fields stores our attribute information; We verify this with the MachO file;

First, we find the structure information for the FieldDescriptor in the Swift source code:

class FieldDescriptor {
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> Superclass;
  const FieldDescriptorKind Kind;
  const uint16_t FieldRecordSize;
  const uint32_t NumFields;
  const FieldRecords<FieldRecord>}Copy the code
  • MangledTypeName: mixed type name;
  • NumFields: Number of current attributes;
  • FieldRecords: Attribute information

FieldRecords is an array consisting of FieldRecord elements with the following data structure:

class FieldRecord {  
  const FieldRecordFlags Flags;
  const RelativeDirectPointer<const char> MangledTypeName;
  const RelativeDirectPointer<const char> FieldName;
}
Copy the code
  • MangledTypeName: Type information of the attribute;
  • FieldName: Attribute name;

Based on the above data structure, we can find the location of the attribute in MachO through the MachO file;

We will generate the MachO file with the above code:

So the store here is TargetClassDescriptor, so we calculate 0xFFFFFEC4 + 0x3F5C = 0x100003E20, we subtract the virtual address, we get 0x3E20, we locate that address in MachO:

Depending on the data structure of the TargetClassDescriptor, we want to find a FieldDescriptor that is offset back by 4*4 bytes:

So here we start storing the offset address of the FieldDescriptor, and we locate the offset address in the MachO by 0x3E30 + 0x00000104 = 0x3F34:

This is where the contents of the FieldDescriptor structure are stored. Based on the data structure, we can see that to find the FieldRecords, we need to offset 16 bytes back:

The subsequent contiguous storage space is the structure of the FieldRecords we need to find. Based on the FieldRecords data structure, we know that the 0xFFFFFFDF offset address is the name of the first attribute. By calculating 0xFFFFFFDF + 0x3F44 + 0x8(0x4F44 is offset back by 8 bytes) = 0x100003F2B, We locate 3F2B in MachO:

We find age and age1 here; 61, 67, 65 and 31 correspond to the ASCII codes of A, G, E and 1 respectively.