Series of articles:OC Basic principle series.OC Basic knowledge series.Swift bottom exploration series.IOS Advanced advanced series

preface

I originally planned to talk about the content of dynamic library in this article, but due to the recent busy project, so I decided to write some content about Swift, interested students can have a look. This article focuses on why structs are value types and classes are reference types

Value types

Prerequisite: You need to know about the five areas of memory, you can refer to this articleMemory management (MEMORY partition), as shown in the figure below:

  • The stack address is larger than the heap address
  • The stack is fromHigh address -> low address, extending downward fromsystemAutomatic management is a piece of continuous memory space
  • The heap is fromLow address -> high address, extending upward bydevelopersManagement, heap space structure is similar toThe list, it isdiscontinuousthe
  • In daily developmentThe overflowRefers to theStack overflow, can be understood as the collision of stack and heap boundaries
  • Global area, constant areaAre stored in theMach-OIn the__TEXT cStringPeriod of

Let’s use an example to introduce what is a value typeAs you can see from the example, age is stored on the stack

  • To viewageMemory condition, as can be seen from the figure, stack areaDirect memoryisvalue
    • Get stack address of age:po withUnsafePointer(to: &age){print($0)}
    • Age memory:x/8gx 0x00007ffeefbff530

  • To viewage1As can be seen from the graph below,age1The assignment ofageTake the value of, and assign it toage1. Among themThe age and age1The address of the8 bytesIt can be explained hereStack space is continuous, and isFrom high to lowthe

So age is the value type

Value type characteristics

  • 1. Yes is stored in the addressvalue
  • 2. Value type transfer process, equivalent topassaA copy of theThat’s what’s calledDeep copy
  • 3.Value passedProcess, andUnshared state

The structure of the body

The usual way of writing a structure

In structs, compilation does not report errors if you do not give default values to attributes. That is, a property may or may not be assigned in a structure

Why are structs value types

Define a structure and analyze it

Print t: Po t, as can be seen from the figure below, the print of t is the value directly, without any information related to the address

  • Get t’s memory address and view its memory
    • Obtain address:po withUnsafePointer(to: &t){print($0)}
    • Check memory:x/8gx 0x0000000100003050

Question: If t is assigned to T1, will t change if T1 is changed?

  • Print t and T1 directly, and you can see that t does not change because t1 changes, mainly becauseT1 and tIs betweenValue passed, that t1 and T are different memory space, is directlytIn theCopy the valuetot1In the.T1 Changed memory space, it isIt doesn't affect the memory space of Tthe

SIL verification

Similarly, we can verify that a structure is a value type by analyzing SIL

  • inSILIn the file, we look at the initialization method of the structure, and we can seeOnly the initAnd theThere is no mallocIn which there is no allocation of heap areas

conclusion

  • Structs are value typesAnd the address of the structure is the memory address of the first member
  • Value types
    • Directly in memoryStored value
    • An assignment of a value type is aValue passed“, which is equivalent to a copy of a copy, into different memory space, two Spaces with each otherUnshared state
    • Value passedIn fact, isDeep copy

Reference types

class

  • The common way to write class

  • In a class, if the attribute is not assigned and is not optional, the compilation will report an error

  • You need to implement it yourselfinitmethods

Why are classes reference types?

Define a class, illustrated by an example

Class initialization object T, stored in the global area

  • printS, tAs can be seen from the picture,sWhat’s in memory space isaddress.tIs stored invalue

  • Gets the address of the S variable and looks at its memory
    • To obtainsPointer address:po withUnsafePointer(to: &s){print($0)}
    • Query s global address memory:x/8gx 0x0000000100003240
    • Query the heap address memory stored in s address:x/8gx 0x0000000100677c70

Characteristics of reference types

  • 1. Yes is stored in the addressThe heap area address
  • 2.The heap area addressIs stored invalue

Q 1: If t1 is assigned to T2, will t1 be changed if T2 is changed?

According to LLDB debugging, changing T2 will cause T1 to change, mainly because the same heap address is stored in BOTH T2 and T1 addresses. If the same heap address is changed, changing T2 will cause T1 to be changed at the same time, that is, the shallow copy

Q 2: If the structure contains class objects, will t change if the instance object properties in T1 are modified?

As can be seen from the printed result, if the attributes of the instance object in T1 are modified, the attributes of the instance object in T will change. Although the value is passed in the structure, the address is still passed in the teacher because it is a reference type

This can also be verified by LLDB debugging

  • Print the address of T:po withUnsafePointer(to: &t){print($0)}
  • Print the memory of t:x/8gx 0x0000000100008380
  • Print the memory situation of teacher address in T:x/8gx 0x0000000106210be0

Note: When writing code, you should tryAvoid value types containing reference types

View currentSILDocuments, althoughLjTeacherIt’s in the value type, and in passing, whether it’s passing or assigning,teacherThis is theReference countingadministrated You can printstudentTo verify our statement, where student’s reference count is3 Mainly because:

  • mainIn theretainAt a time
  • student.gettermethodsretainAt a time
  • student.setterIn the methodretainAt a time

mutating

Define a stack through the structure, mainly push, pop methods, we need to dynamically modify the stack array

  • If the following method is used, an error will be reported directly becauseThe value type itself is not allowed to modify attributesthe

  • Change the push method to the following and checkSILIn the filepushfunction

As can be seen from the figure, in addition to item, the push function also has a default parameter, self, which is of let type, indicating that modification is not allowed

  • Try 1: If I change the push function to look like this, can I add it?

The print result is as followsYou can derive the code union aboveCannot add itemBecause theS is another structure object, which is equivalent toCopy the value, then callPush is an array that adds item to SIn the

  • Add push to push based on the previous errormutatingFound ready to add to array

  • Look at its SIL file, find the push function, and see that it’s different,pushaddmutatingAfter (only for value types) is essentially givenValue type functionaddedinoutKeyword, equivalent to in the process of value passing,passisreference(i.e. address)

Inout keywords

In general, the default parameters in the function declaration are immutable. If you want to change them directly, you need to add the inout keyword to the parameters

  • withoutinoutKeyword, assign value to parameter, compile error

  • addinoutKeyword that can be assigned to parameters

conclusion

  • 1. If you want to modify the properties of a function in a structure, add the prefix to the functionmutatingClass does not
  • 2.mutatingEssentially plus oneInout modifies self
  • 3.InoutThe equivalent ofTake the addressCan be understood asaddressThat reference
  • 4.mutatingmodifiedmethodsAnd theinoutmodifiedparameter

conclusion

View the memory model of struct & class with LLDB.

  • valueType, equivalent to oneThe local fileWhen we send you a file over the network, it’s like a value type, what did you change that we don’t know
  • referenceType, equivalent to oneOnline fileWhen we edit a file with you, it acts as a reference type and both sides see the changes
  • The structure of the bodyIn theFunction modifies attributes, need to be added before the functionmutatingKeyword, essentially adding the default argument self to the functioninoutKeyword, willselffromletThe constant is changed tovarvariable

Method dispatch

From the above analysis, we have the following question: Where are the methods of structs and classes stored? Let’s analyze them one by one

Static distributed

The function of a value object is called statically, that is, directly from the address, calling a function pointer, which is determined after compilation and linking. The function pointer is stored in the code snippet, while the structure does not hold methods. So it can be called directly from the address

  • Struct function debugging is shown below

  • Open Open the demoMach-OExecutable file, of which__textSegments, called code segments, are where the assembly instructions that need to be executed are

  • There is a problem with the above analysis: the direct address call is followed bysymbolWhere did this symbol come from?

fromMach-OIn the fileSymbol Tables, butStrings are not stored in symbol tables, the string is stored inString Table (a list of strings containing all variable and function names, stored as strings), and then search for the corresponding character in the string according to the offset value in the symbol table, and then rename the character:Project name + class name + function name, as shown below

  • Symbol Table: The location of the stored symbol in the string table
  • Dynamic Symbol Table:Dynamic library functionOffset information in the symbol table

You can also obtain the symbol table in the project through the terminal command nm

  • View the symbol table:Nm Mach-o file path
  • Restore symbol names by command:Xcrun swift - demangle symbols

  • If you haveedit scheme -> runIn thedebugtorelease, in the executable directory, an additional suffix isdSYMIn this case, go to the Mach-o fileteach, found to be unable to find, the main reason is becauseStatically linked functions, in fact, do not need symbolsOnce the compilation is complete, its address is determined after the current oneThe symbol table deletes the symbol corresponding to the current function, in the release environment,The symbol tableIs stored inCould not determine the symbol of the address
  • For symbols that cannot determine the address, it is inRuntime determinationWhen the function is called for the first time (equivalent toLazy loading), for example print, is passeddyld_stub_bindaddressable

Function symbol naming rules

  • forC functions(C does not allow function overloading because there is no way to tell the difference)

  • In the case of OC, function overloading is also not supported, and its symbolic naming is-[class name function name]

  • For Swift, yesFunction overloadingMainly because of swiftRenormalization naming ruleIt’s a little bit complicated, yesMake sure the function symbol is unique

Dynamic dispatch

Assembly instruction supplement

  • blr: jump instruction with return, jump to instruction followed by address saved in register
  • mov: Copy a value from one register to another register (used only between registers or between registers and constants, not memory addresses)
    • Mov x1, x0 copies the value of register x0 into register x1
  • ldr: Reads a value from memory into a register
    • LDR x0, [x1, x2] add register x1 and register x2 as the address, take the value of the memory address into register X0
  • str: Writes a value in a register to memory
    • STR x0, [x0, x8] saves the value of register x0 to memory at [x0 + x8]
  • bl: Jumps to an address

Explore the scheduling of classes

First, the format of V_Table in SIL files

Decl ::= sil-vtable // Sil Vtable contains keywords, identifiers (i.e. class names), all methods sil-vtable ::= 'sil_vtable' identifier '{' Sil-vtable-entry * '}' // The method contains the declaration and function name sil-vtable-entry ::= sil-decl-ref ':' SIL-linkage? sil-function-nameCopy the code

For example, in the case of LjTeacher, the V-tabler in SIL is shown in the figure below

  • sil_vtableKey words:
  • LjTeacher: represents the function table of the LjTeacher class
  • Second, the declaration of the current method corresponds to the name of the method
  • The table of functions can be interpreted asAn array ofDeclare a method inside a class without any keyword modificationContinuous depositIn our current address space. This point can be verified by the breakpoint

  • register read/x raxFor the time of,Address is the same as the address of the instance object, includingraxThe address of the instance object, the first address

  • If you look at the offset addresses of the following methods, you can find that the methods are stored consecutively and correspond exactlyV-TableIn the function tableDischarge of the order, that is, in the order defined in the table of functions

Function table source exploration

Let’s explore the source code at the bottom of the function table

  • Source searchinitClassVTable, and add breakpoints, and then write source code for debugging

It is encoded internally by the for loop, and offset+index is offset. Then method is obtained and stored in the offset memory, from which we can verify that the function is continuously stored

For functions in class, class method scheduling is via v-taable, which is essentially a contiguous memory space (array structure).

Question: What if you change the location of the method declaration? For example, the function in Extension, is the function scheduling mode still function table scheduling?

Verify with the following code

  • To define aThe extension of LjTeacher

  • Defining a subclassLjYoungTeacher is inherited from LjTeacherTo view the V-table in the SIL

  • To viewSILFile, foundSubclasses inherit only the functions defined in class, the function in the function table

If the extension function is also in the function table, it means that the subclass also has the function table, but the subclass can not have the related pointer to record whether the function is the parent class method or the subclass method. So you don’t know where to insert the method, so functions in Extension can’t be safely put into subclasses. Therefore, it can be proved that the method in Extension is called directly and belongs only to the class, and the subclass cannot inherit

Development considerations

  • Inheriting methods and properties,Can't write the extensionIn the.
  • whileextensionThe function created in the., must belong to its own class, but itsSubclasses also have access, justCannot inherit and overwrite, as shown below

Final, @objc, dynamic modifier functions

The final modification

  • finalThe way to modify it isDirect dispatchingCan be verified by SIL validation + breakpoint validation

@ objc modification

use@objcThe key word is willswiftThe method in is exposed to OCFound through SIL+ breakpoint debugging@objcThe way to modify it isFunction table scheduling 【 Tips 】 :mixedHeader file viewing mode: ViewThe project name - Swift. HThe header file

  • OC still wouldn’t be able to call swift if it was just using the @objc modifier, so if you wanted toOC access swiftClass requires inheritanceNSObject

The dynamic modification

Take the following code as an exampledynamicModifies how a function is scheduledWherein, the scheduling of teach function isFunction table scheduling, can be verified through breakpoint debugging, usingdynamicIt means yesThe dynamic change, meaning that classes can be used when they inherit from NSObjectmethod-swizzling

@objc + dynamic

Through breakpoint debugging, go isobjc_msgSendFlow, movingState message forwarding

Scenario: Implement method exchange in SWIFT

  • Before functions that need to be swapped in SwiftdynamicModify, and then pass:@_dynamicreplacement (for: function symbol)Swap, as shown below

Replace the Teach method with Teach 5

  • If teach is not implemented/if removeddynamicModifier, an error is reported

conclusion

  • structisvalueType, where the function scheduling belongs toDirect call address, i.e.,The static scheduling
  • classisreferenceType in which function scheduling is passedV - Table function TableFor scheduling, i.eDynamic scheduling
  • extensionThe function scheduling mode inDirect dispatching
  • finalThe modified function scheduling mode isDirect dispatching
  • @objcThe modified function scheduling mode isFunction table schedulingClass must also be used if OC is requiredInheritance NSObject
  • dynamicThe scheduling mode of the modified function isFunction table schedulingTo make the function dynamic
  • @objc + dynamicCombinatorial modification of function scheduling that is performed isobjc_msgSendProcess, i.e.,Dynamic message forwarding

Wrote last

The next article continues to write high order advanced series, introduces the content of dynamic library, the following will be based on the project to talk about dynamic library merge and other content, Swift article will be a supplement when the project is busy, because Swift does not have so many complex operations, also need not write shell language! Welcome everyone to leave a comment, also hope you like a lot of support. I hope we can communicate with each other, explore and make progress together