Nature of object

1. The clang

  • Clang is a lightweight C/C++/Objective-C compiler written by Apple and based on LLVM. The source code is published under the LLVM BSD protocol. Clang will support its normal lambda expressions, simplified handling of return types, and better handling of constEXPr keywords.

  • It is almost completely compatible with the GNU C language specification (although there are some incompatibables, including some differences in compiler command options) and adds additional syntactical features such as C function overloading (which modifies functions by __attribute__((overloadable)), One of its goals is to go beyond GCC.

  • It is mainly used for low-level compilation, output some OC files into C++ files, such as main.m output into main. CPP, its purpose is to better observe some low-level structure and implementation logic, easy to understand the underlying principle

2. Clang operation instruction

CPP clang-rewrite-objc main.m -o main.cpp // UIKit error -- compile viewController.m to viewController.cpp clang-rewrite-objc - fobjc - arc - fobjc - runtime = ios - 13.0.0 - isysroot/Applications/Xcode. The app/Contents/Developer/Platforms / IPhoneSimulator. Platform/Developer/SDKs/iPhoneSimulator14.0. SDK ViewController. M / / ` xcode installed ` when passing the ` xcrun ` command, The 'xcrun' command does some wrapping on top of 'clang', To better use some xcrun-sdk iphonesimulator clang-arch arm64-rewrite-objc main.m -o main-arm64.cpp (emulator) xcrun-sdk iphoneOS Clang-arch arm64-rewrite-objc main.m-o mainarm64.cpp (mobile phone)Copy the code

3. Explore the nature of the object

  • Write the test code in the main function first

  • From the terminal, use clang to compile main.m into main. CPP, and enter the following command on the terminal
xcrun -sdk iphonesimulator clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp
Copy the code
  • Open the compiled main-arm64.cpp and look for the LGPerson definition. The LGPerson will be compiled into a struct structure underneath
@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

As shown in the figure below

From the compiled main-arm64.cpp we can see:

  • The underlying implementation of NSObject is really just a structure that contains a pointer to ISA.

  • Class is essentially a pointer to an objC_class structure.

  • The LGPerson_IMPL structure has three member variables:

  • Isa inherits from NSObject

  • KCName

  • _name

  • For the property name: the underlying compilation generates the corresponding setter(I_LGPerson_setName, which calls the objc_setProperty method), getter(_I_LGPerson_name) methods and converts them to _name for us

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

  • The initInstanceIsa method, one of the core alloc methods, is initialized using the isa_t type.
  • 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 conversion of AS in Swift. Isa’s strong transformation in the source code is shown below

Conclusion From the above exploration process, it can be concluded that:

  • OC objects are essentially structures

  • The ISA in LGPerson is inherited from the ISA in NSObject

How CLS relates to classes

Union

There are two ways to construct data types:
  • Structure (struct)
  • A union (also called a Commons)

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, which is a waste of memory. Suppose you have 4 int members and you allocate 16 bytes of memory, but when you use them, you only use 4 bytes. The remaining 12 bytes are wasted memory

  • Advantages: Large storage capacity, strong inclusiveness, and members do not affect each other

Consortium Consortium 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

  • Disadvantages: 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

Each member of a structure occupies different memory. All members of the structure occupy the same memory segment. Modifying one member affects all other members

  • Memory allocation size

Structure memory >= total memory used by all members (there may be gaps between members) The memory used by the Commons is equal to the memory used by the largest member

The type of ISA is ISA_t

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

Isa_t () {} ISA_t (uintptr_t value) : bits(value) {} uintptr_t bits; #if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // defined in isa.h }; #endif };Copy the code

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). In general, isa Pointers take up 8 bytes of memory, or 64 bits, which is enough to store a lot of information. This can save a lot of memory and improve performance, as shown in isa_t’s definition:

  • 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

    Initialized by CLS, bits has no default value

    With bits initialization, CLS has default values

  • The constructor member ISA_BITFIELD, which is a macro definition, comes in two versions: ARM64 (for ios mobile) and x86_64 (for macOS). Here are some of their macro definitions, as shown in the figure below

Exploration of ISA Principle

Alloc –> _objc_rootAlloc –> callAlloc –> _objc_rootAllocWithZone –> _class_createInstanceFromZone method path, Find initInstanceIsa and enter its implementation

objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { ASSERT(! cls->instancesRequireRawIsa()); ASSERT(hasCxxDtor == cls->hasCxxDtor()); // Initialize isa initIsa(CLS, true, hasCxxDtor); }Copy the code

InitIsa analysis

  • Isa_t newisa(0) is the equivalent of initializing this isa thing, newisa. Equivalent to assigning an attribute to ISA.
  • SUPPORT_INDEXED_ISA applies to WatchOS. Isa isa consortium and is mutually exclusive. CLS and bits are elements of isa. If nonpointer=true, CLS is assigned; if false, bits is assigned.

Verifying isa pointer bit fields (0-64)

Based on the 0-64 bit fields mentioned earlier, you can prove these bits in the ISA pointer here using the initIsa method (currently on macOS, so x86_64 is used).

  • Isa initialization is done using the TCJPerson breakpoint in main –> initInstanceIsa –> initIsa –> ISA_t newisa(0).
  • Run the LLDB command: p newisa to obtain details about newisa

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