Summary of basic principles of iOS

In the alloc & Init & New article, alloc has three core operations, one of which is obj->initInstanceIsa: the class is associated with ISA, and that’s what we’ll explore today.

The main purpose of this article is to understand how classes relate to ISA. Before we get into the body, we need to understand one concept: What is the nature of OC objects?

OC Object nature

Before we explore the nature of OC objects, let’s look at one compiler: Clang

Clang

  • clangIs aAppleLead writing, based onLLVMtheC/C++/OC compiler
  • Chiefly used ofThe underlying compilerTo someThe output fileintoC + + files, e.g.main.mThe output intomain.cppThe purpose is for better observationThe underlyingSome of thestructure 及 implementationIt is convenient to understand the underlying principle.

Exploring the nature of objects

  • inmainTo customize a classLGPerson, has an attribute name
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LGPerson
@end
Copy the code
  • Through the terminal, usingclangwillmain.mCompiled intomain.cppThere are several build commands, the first of which is used here
CPP clang -rewrite-objc main.m -o main. CPP //2, viewController.m to viewController.cpp clang -rewrite-objc-fobjc-arc-fobjc-Runtime =ios-13.0.0 -isysroot / / Applications/Xcode. App/Contents/Developer/Platforms/iPhoneSimulator platform/Developer/SDKs/iPhoneSimulator13.7. The SDK Viewcontroller.m // The following two ways are through the command line specifying the schema, Use the xcode tool xcrun //3, emulator file compilation -xcrun -sdk iphonesimulator clang -arch arm64-rewrite-objc main.m -o main-arm64.cpp Iphoneos clang -arch arm64 - rerewrite -objc main.m -o main-arm64.cppCopy the code
  • Open the compiled main. CPP, find the LGPerson definition, and see that LGPerson is compiled into a struct structure underneath

  • So the first property in LGPerson_IMPL is actually isa, and it inherits from NSObject, and it’s pseudo-inheritance, and the pseudo-inheritance is by directly defining the NSObject structure as the first property in LGPerson, That means LGPerson owns all the member variables in NSObject.

  • The first NSObject_IVARS property in LGPerson is equivalent to isa in NSObject

@interface NSObject <NSObject> {Class ISA OBJC_ISA_AVAILABILITY; } struct NSObject_IMPL {Class isa; }; Struct LGPerson_IMPL {struct NSObject_IMPL NSObject_IVARS; // equivalent to Class isa; NSString *_name; };Copy the code
  • Clang compilation results, as shown below:

From the above analysis, you understand the nature of the OC object, but looking at the definition of NSObject raises a question: why is isa of type Class?

  • InitInstanceIsa (int int, int int, int int, int int, int int, int int, int int, int int, int int, int int, int int, int int, int int);

  • In NSObject, ISA is defined as Class. The fundamental reason is that ISA sends back Class information. In order to make developers more clear, isa needs to perform a type cast when it returns, similar to the strong cast of AS in Swift. Isa’s strong transformation in the source code is shown below

conclusion

Therefore, it can be concluded from the above exploration process:

  • The nature of OC objectsIn fact, isThe structure of the body
  • LGPersonIn theisaIs inherited fromNSObjectIn theisa

Objc_setProperty source code exploration

In addition to LGPersong’s underlying definition, we also find set and GET methods for the property name, as shown below, where the implementation of the set method depends on the objc_setProperty in the Runtime.

  • You can follow these steps to unravel the underlying implementation of objc_setProperty

  • Search objc_setProperty globally to find the source implementation of objc_setProperty

Enter the source code implementation of reallySetProperty. The method is based on the new value retain and the old value release

conclusion

After exploring the underlying source code of objc_setProperty, the following points emerge:

  • objc_setPropertyThe purpose of the method applies toCorrelation of the upperthesetMethods andThe underlyingthesetMethod, in essence, is ainterface
  • It’s designed that waywhyIs that,The upperthesetThere are many ways ifCall the underlying set directlyMethod, it produces a lot of omegaTemporary variableWhen you want toTo find theAn SEL is going to be verytrouble
  • For these reasons, Apple has adopted itAdapter design pattern (which ADAPTS the underlying interface to the one the client needs).foreignTo provide ainterface, which is used by the upper-level set method,internalCalling the underlyingSet method, so that they are not affected by each other, i.eNo matter what happens at the top, the bottom stays the same, rightOr,What happens at the bottom doesn't affect what happens at the topTo isolate the upper and lower interfaces

The following figure shows the relationship between the top layer, the isolation layer, and the bottom layer

How CLS relates to classes

Alloc, init, and new: alloc, init, and new: alloc, init, and new

Before we do that, we need to know what isa union and why is isa’s type isa_t defined as a union

Union

There are two ways to construct data types:

  • The structure of the body(struct)
  • A consortium(union, also known asThe appropriate)

Structure STRUCTURE refers to the combination of different data into a whole, the variables are coexisting, regardless of whether the variables are used, the allocation of memory.

  • Disadvantages: All attributes are allocated memory, compareWaste of memory, suppose there are four int members allocated16Bytes of memory, but when used, you only use it4Bytes, the rest12Bytes are a waste of memory
  • Pros: StorageCapacity is larger.receptiveAnd between membersThey don't affect each other

A consortium

Union is also composed of different data types, but its variables are mutually exclusive, and all the members occupy a segment of memory. Moreover, the common body adopts the memory overwrite technology, which can only store the value of one member at a time. If a value is assigned to a new member, the value of the original member will be overwritten

  • Weakness: Weak tolerance
  • Advantages: All members share a segment of memory, which makes memory usage more delicate and flexible and saves memory space

The difference between the two

  • Memory usage

    • Structure of theEach member occupies different memoryBetween each otherHave no effect on
    • Share bodyAll members occupy the same memory segment, modify a member willimpactAll remaining members
  • Memory allocation size

    • Structure in vivo> =All members occupiedThe sum of memory(There may be gaps between members)
    • The appropriateThe amount ofmemoryIs equal to theLargest memberThe amount ofmemory

The type of ISA is ISA_t

The following is the definition of isa pointer type ISA_t, which is defined by union.

The reason for the use of union for the ISA_t type is also based on memory optimization, which is implemented in the ISA pointer through the principle of char + bitfield (that is, each bit in binary can represent different information). Typically, isa Pointers take up 8 bytes of memory, or 64 bits, which is enough to store a lot of information and can result in significant memory savings to improve performance

It can be seen from the definition of ISA_t:

  • It provides two members, CLS and bits, which are mutually exclusive, as defined by the union, meaning that there are two ways to initialize isa Pointers

    • throughclsInitialization,Bits No default value
    • throughbitsInitialization,CLS has default values
  • The constructor member ISA_BITFIELD, which is a macro definition with two versions __arm64__ (for ios mobile) and __x86_64__ (for macOS). Here are some of their macro definitions. As shown in the figure below

    • Nonpointer Specifies whether to enable pointer optimization for isa Pointers. It has two values, representing custom classes and so on

      • 0:Pure isa pointer
      • 1: not onlyClass object addressIsa containsThe class informationAnd the object’sReference countingEtc.
    • Has_assoc represents the associated object flag bit, which is 1 bit

      • 0:There is no relationshipobject
      • 1:A link betweenobject
    • Has_cxx_dtor indicates whether the object has a C++/OC destructor (similar to dealloc), with 1 bit

      • ifThere areDestructor, is requiredDo the destructorlogic
      • ifThere is no, can be fasterThe release ofobject
    • Shiftcls represents the value of the pointer to the stored class (the address of the class), that is, the class information

      • arm64A majority of the33Bit to turn on pointer optimization in case of arm64 architecture33Bits are used to store class Pointers
      • x86_64A majority of the44position
    • Magic is used by the debugger to determine whether the current object is a real object or not initialized. It has six bits

    • Weakly_refrenced is a weak variable of whether an object is or has been pointed to an ARC

      • Objects without weak references can be released faster
    • The DealLocating object indicates whether memory is being freed

    • Has_sidetable_rc indicates that when the object reference count is greater than 10, the variable is borrowed to store carry

    • Extra_rc (Extra reference count), which represents the reference count value of this object, is actually the reference count value minus 1

      • If the object’sThe reference count is 10, thenextra_rc9 (this is for example only), which is actually true for the iPhoneextra_rcIt uses 19 bits to store reference counts

For the two different platforms, the isa storage situation is shown in the figure

The principle of exploring

  • throughalloc --> _objc_rootAlloc --> callAlloc --> _objc_rootAllocWithZone --> _class_createInstanceFromZoneMethod path, found toinitInstanceIsaAnd into its principle of implementation

Enter the source implementation of the initIsa method, primarily to initialize the ISA pointer

The logic of this method is divided into two parts

  • throughclsInitialize theisa
  • throughbitsInitialize theisa

Verifying isa pointer bit fields (0-64)

From the 0-64 bit fields mentioned above, you can prove here that isa pointer has these bit fields in the initIsa method (currently on macOS, so x86_64 is used).

Init with the LGPerson breakpoint in main –> initInstanceIsa –> initIsa –> go to ISA in else

To continue, go to newisa.bits = ISA_MAGIC_VALUE; On the next line, assign a value to the bits member of ISA and re-execute the LLDB command p newisa. The result is as follows

Magic is 59 because the ISA pointer address is converted into binary, 6 bits are read from 47 (because there are 4 bit fields in front, 47 bits are occupied, and the address starts from 0), and then converted into decimal, as shown in the figure below

Isa’s association with classes

The principle of CLS association with ISA is that the shiftCls bit field in ISA pointer stores the class information, where initInstanceIsa process is to associate the Calloc pointer with the current CLS class. There are the following verification methods:

  • [Method 1] ThroughinitIsaIn the methodnewisa.shiftcls = (uintptr_t)cls >> 3;validation
  • [Method 2] PassIsa pointer addresswithISA_MSAKThe value of the&To verify the
  • [Mode 3] Through the Runtime methodobject_getClassvalidation
  • [Mode 4] PassAn operationvalidation

Method 1: Use the initIsa method

Run the preceding shiftcls = (uintptr_t)newCls >> 3, where Shiftcls stores the value information of the current class

  • At this point to seecls, it isLGPersonclass
  • shiftclsThe logic of assignment is to assignLGPersonAfter coding,Moves to the right. 3position

Execute the LLDB command p (Uintptr_t) CLS and the result is (uintptr_t) $8 = 4295000992. Move it three places right in either of the following ways to get 536875124 and store it in shiftcls of Newisa

  • p (uintptr_t)newCls >> 3
  • Through the results of the previous step$8Run the LLDB commandp $8 >> 3

  • Continue executing the program toisa = newisa;Part is executed at this timep newisa

There are two changes in the bit field of bits compared to the result of the bits assignment

  • cls 由The default valueAnd turned intoLGPersonThat will beIsa and CLS are perfectly correlated
  • shiftclsby0Turned out to be536875124

So isa changes the value of the initialized member, as shown in the figure below

Why do I need a strong type when SHIFtcls is assigned?

Since the memory store cannot store strings, machine code can only recognize 0 and 1, so it needs to be converted to uintptr_t data type so that the class information stored in ShiftCLs can be understood by machine code, where Uintptr_t is long

This is mainly because Shiftcls is in the middle of the ISA pointer address, and there are three bit fields in front of it. In order not to affect the data in the first three bit fields, it needs to be zeroed by right shift.

Method 2: Use ISA & ISA_MSAK

  • After method one, proceed to return_class_createInstanceFromZoneMethod, at this pointCLS and ISA have been associated, the implementation ofpo objc
  • performx/4gx obj,isaAddress of pointer0x011d8001000083a1

The ISA pointer address & ISA_MASK (in macOS, defined using macros in X86_64), i.e. Po 0x011D8001000083a1&0x00007ffffFFFFff8, gives LGPerson

  • arm64, the value defined by the ISA_MASK macro is0x0000000ffffffff8ULL
  • x86_64, the value defined by the ISA_MASK macro is0x00007ffffffffff8ULL

Method 3: Use object_getClass

You can also verify isa’s association with a class by viewing object_getClass’s source code implementation. There are several steps:

  • #import
  • throughruntimeAPI, namelyobject_getClassFunction to get class information
object_getClass(<#id  _Nullable obj#>)
Copy the code
  • To viewobject_getClassFunction source code implementation

  • Enter thegetIsaSource code implementation

  • Click on theISA(), enter the source code

  • Click on thegetDecodedClass, enter the source code

  • Click on thegetClass, enter the source code

  • inelseIn the process, getisathebitsThis place, and then& ISA_MASK, which is consistent with the principle in mode two,Get the current class information
  • You can do it from hereCLS and ISA have been perfectly correlated

Mode 4: Through bit operation

  • Go back to_class_createInstanceFromZoneMethods. throughx/4gx objgetobjThe current class information is stored inisaPointer, and isashiftclsAt this time of44Bit (because of being inmacOSEnvironment)

If you want to read the 44-bit information in the middle, you need to go through the bit operation, erase the three bits on the right and the part on the left except the 44-bit, and its relative position is unchanged. Its bit operation process is shown in the figure, where Shiftcls is the class information to be read

  • Move the ISA address right by 3 bits: P /x 0x011D8001000083A1 >> 3 to get 0x0023B00020001074

  • In the obtained 0x0023B00020001074, shift 20 bits left: P /x 0x0045138768236660 << 20, get 0x0002000107400000

  • Why 20 to the left? Because we moved 3 to the right, we moved 3 to the right, and we had 17 zeroes to erase, so we had to move 20

  • Move the resulting 0x0002000107400000 17 bits right: P /x 0x0002000107400000 >> 17 to get the new 0x00000001000083A0

  • P /x CLS also produces 0x00000001000083A0, so it can be proved that CLS is associated with ISA