1. iOS underlying analysis alloc analysis

1, alloc init exploration

Introduction:

In iOS, alloc is used to open memory for object applications. The most commonly used form for initialization is as follows.

[[xxx alloc] init];

Question:

1. Where and how to analyze alloc?

2. What does alloc do at the bottom? How is it implemented at the bottom?

3. The object has been created in alloc, and nothing is written in init (only after reading the source code), so what exactly is init used for?

 

Analysis:

Explore common methods of alloc


Explore alloc starting with int main function

Here are three common ways to explore

1) , lower breakpoint: control + in to find objc > alloc

2) , lower symbol breakpoint: libobjc.A.dylib`+[NSObject alloc]

Breakpoint – symbolic Breakpoint – corresponding method name

3) . assembly and viewing process (pay attention to debugging with real machine)

Dubug - Debug workflow - Always show Disassembly

Download source code and environment configuration

You need to download and compile in the source code to further explore alloc. The configuration is as follows

objc4-750 source code + xcode11 + Mac OS 10.15

Official source download address

The latest source compilation and debugging of iOS_objc4-756.2

 

Start problem analysis

alloc init

- (void)viewDidLoad {
    [super viewDidLoad];
    NSLog(@"LG_Cooci: alloc explore");
    // alloc exploration
    // alloc has created the object init
    // How is alloc created?
    // alloc implementation principle source code implementation
    // Introduce three ways
    // libobjc.A.dylib
    // 1: Lower breakpoint: control + in - objc \ alloc
    // 2: Lower symbol breakpoint: libobjc.A.dylib`+[NSObject alloc]:
    // 3: Assembly libobjc. A.dylib ` objc \ alloc:
    
    LGPerson *p1 = [LGPerson alloc];
    LGPerson *p2 = [p1 init];
    LGPerson *p3 = [p1 init];
    LGNSLog(@"%@ - %p",p1,&p1);
    LGNSLog(@"%@ - %p",p2,&p2);
    LGNSLog(@"%@ - %p",p3,&p3);

//The printed result is p1 p2 p3. The result is the same and the address is different

//There are three pointers and three pointer addresses. But the three pointers point to the same address.

3 Pointers to the same space

}

At this time, we can know that p1 p2 p3 three pointers point to the space opened by [LGPerson alloc]

At this time, there will be the first question, that is, alloc has created the object, and what does init do? , this is the beginning of alloc analysis

alloc analysis

Start with int main function, break the point, and then command + left click to go to step-by-step analysis (analysis in the source code)

1,alloc

NSObject.mm

+ (id)alloc {
    return _objc_rootAlloc(self);
}

 2,_objc_rootAlloc

NSObject.mm returns a callAlloc

// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

3,callAlloc(cls,falsee,true)

NSObject.mm

// Call [cls alloc] or [cls allocWithZone:nil], with appropriate 
// shortcutting optimizations.
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
    if (slowpath(checkNil && !cls)) return nil;//Fault tolerance is useless.

#if __OBJC2__
    if (fastpath(!cls->ISA()->hasCustomAWZ())) {
        //None of the above is important. If you want to judge a non custom class, go here. These are the key points below
        // No alloc/allocWithZone implementation. Go straight to the allocator.
        // fixme store hasCustomAWZ in the non-meta class and 
        // add it to canAllocFast's summary
        if (fastpath(cls->canAllocFast())) {
            // No ctors, raw isa, etc. Go straight to the metal.
            bool dtor = cls->hasCxxDtor();
            id obj = (id)calloc(1, cls->bits.fastInstanceSize());
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            obj->initInstanceIsa(cls, dtor);
            return obj;
        }
        else {
            // Has ctor or raw isa or something. Use the slower path.
            id obj = class_createInstance(cls, 0);
            if (slowpath(!obj)) return callBadAllocHandler(cls);
            return obj;
        }
    }
#endif
//At present, the real machine uses objc-2, so the following is not going to be omitted, not the point
}

4,class_createInstance(cls, 0) (create instance)

It internally calls the "class" createinstancefromzone method, in which size calculation, memory application and isa initialization are performed

id 
class_createInstance(Class cls, size_t extraBytes)
{
    return _class_createInstanceFromZone(cls, extraBytes, nil);
}

5,_class_createInstanceFromZone

According to different conditions, select calloc or malloc ﹣ zone ﹣ calloc to apply for memory, and initialize isa pointer, apply for object obj of size (described in size 5.1), and return

static __attribute__((always_inline)) 
id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, 
                              bool cxxConstruct = true, 
                              size_t *outAllocatedSize = nil)
{
    // Read class's info bits all at once for performance
    bool hasCxxCtor = cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();

    size_t size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (!zone  &&  fast) {
        obj = (id)calloc(1, size);
        if (!obj) return nil;
        obj->initInstanceIsa(cls, hasCxxDtor);
    } 
    else {
        if (zone) {
            obj = (id)malloc_zone_calloc ((malloc_zone_t *)zone, 1, size);
        } else {
            obj = (id)calloc(1, size);
        }
        if (!obj) return nil;
        obj->initIsa(cls);
    }

    if (cxxConstruct && hasCxxCtor) {
        obj = _objc_constructOrFree(obj, cls);
    }

    return obj;
}

5.1. CLS - > instancesize (extrabytes), calculate the size,

In order to facilitate reading and improve performance, byte alignment is carried out, and then the actual size required after alignment is calculated
In 64 bit system, the object size is aligned with 8 bytes, but the minimum memory applied by alloc is 16 bytes, that is to say, the system allocates memory aligned with 16 bytes

8-byte alignment is to exchange space for time to ensure the speed of reading and writing.

// This method is used for byte alignment
uint32_t alignedInstanceSize() {
        return word_align(unalignedInstanceSize());
    }
// The detailed method of byte alignment: word? Mask is 7 under 64 bits and 3 under 32 bits, which is used for byte alignment
static inline size_t word_align(size_t x) {
    return (x + WORD_MASK) & ~WORD_MASK;
}

// Application address, 16 byte limit here
size_t instanceSize(size_t extraBytes) {
        size_t size = alignedInstanceSize() + extraBytes;
        // CF requires all objects be at least 16 bytes.
        if (size < 16) size = 16;
        return size;
}

Conclusion:

alloc

–>_objc_rootAlloc

-> callAlloc

-> class_createInstance (create instance)

->_class_createInstanceFromZone (create instance from private zone)

{1, 「 class 」 instancesize (open space size, 64 bit 8-byte alignment, 32-bit 4-byte alignment and greater than or equal to 16 at the same time)

2,calloc

3,objc_initInstanceIsa (instance Isa)

Attach the flow chart summarized for God

 

Expand and extend other knowledge points

1. Some commands of breakpoint console debugging

All Variables All variables

register read

 

2. When OC compiles to the lower level, it still calls OC method, and then the lower level mixed compilation calls function processing. At the bottom, it's mixed

 

 

3. alloc create object request memory space - pointer (with a pointer is no longer empty class LGPerson)

When alloc creates an object, it will return the corresponding object address. If it returns the object address, it has the ability to apply for memory space. The return value is x0.

x0 is not only the pass port of the first parameter, but also the storage place of the return value.

First open a memory space (the size of the opened memory space is a multiple of 8 bytes (64 bit machine), and at the same time, it should be greater than 16). After the opening is successful, associate the memory space.

isa is a union pointing to its address space.

 

4,alloc

  1. Open application memory
  2. With the initialization of isa

5,init

  1. _objc_rootInit(obj)
  2. return obj

Factory design, give subclass to customize and rewrite free

init is the specification

 

6,new

Equivalent to alloc init

[callAlloc() init]

 

7. System allocated object memory algorithm

Open memory size (and, reverse)

(x+WORO_MASK)&~ WORO_MASK

64 bit define woro mask 7ul 32-bit 3UL

64 is a multiple of 8. 32 bits are a multiple of 4. At the same time, they meet the requirements of 16 or more

if(size < 16) size = 16;

 

8. Memory alignment

SHIFT_NANO_QUANTUM 16
K = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM


slot_bytes = k << SHIFT_NANO_QUANTUM

The system opens up memory in 16 bit multiple.

 

9. Assembly analysis, compiler will optimize part of the code

 

1,     build setting

  1. optimization
  2. Fastsamallest

 

2. The function of compiler optimization

  1. Compile time
  2. Link time
  3. Running time
  4. Free time

 

LLDB debugging configuration objc4 malloc source code

The real reason for debugging is

Need real machine

Arm64 X0- X30

Simulator is

X86

 

 

64 original articles published, 12 praised, 180000 visitors+
Private letter follow

Tags: iOS Mac simulator

Posted on Fri, 17 Jan 2020 23:43:02 -0500 by veeraa2003