preface

Writing code usually involves creating objects, so today we will explore how this process works.

Alloc and init

Let’s look at how alloc and init affect Pointers:They’re printed separately hereObject p.Pointer to the p.Pointer address P, the results are as follows:

As you can see, all three objects point to the same memory space, which I guess is inallocIn memory, andinitThe operation doesn’t. So whether that’s true or not, we’ll see

Tools to prepare

  1. Download objC4-818.2 source code, compile can refer to this man’s article source code compilation debugging
  2. The compiled source code objC4_DEBUG

Alloc source inquiry

The overall flow chart of alloc is as follows

Step 1: Find LGPerson’s alloc in Main and click on the alloc function

+ (id)alloc {
    return _objc_rootAlloc(self);
}
Copy the code

Step 2: Go to the _objc_rootAlloc function

id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/.true/*allocWithZone*/);
}
Copy the code

Step 3: Enter the callAlloc function

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__ // Check whether an OC compiler is available
    // checkNil is passed as nil,! CLS is nil, so slowPath is false
    // slowpath is false and will not go to if
    if(slowpath(checkNil && ! cls))return nil;
    
    // Determine if the parent class or class has a default implementation of alloc/allocWithZone
    // FastPath is true
    if(fastpath(! cls->ISA()->hasCustomAWZ())) {return _objc_rootAllocWithZone(cls, nil);
    }
#endif

    // No shortcuts available.
    if (allocWithZone) {
        return ((id(*) (id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*) (id, SEL))objc_msgSend)(cls, @selector(alloc));
}
Copy the code

Note 1: SlowPath and FastPath

// X is likely to be true, and fastPath can be abbreviated to truth judgment
#define slowpath(x) (__builtin_expect(bool(x), 0))

// slowpath is likely to be false
#define fastpath(x) (__builtin_expect(bool(x), 1)) 
Copy the code

Introduced from the article __builtin_expect description

The __builtin_expect directive was introduced by GCC to allow programmers to tell the compiler which branch is most likely to be executed. It is written __builtin_expect(EXP, N), meaning EXP==N with a high probability

  • __builtin_expect(bool(x), 0)That means it’s more likely that x is false
  • __builtin_expect(bool(x), 1)That means it’s more likely that x is true

  • slowpathfastpathIs used to optimize the compiler to reduce instruction jumps and performance degradation
  • Performance optimization can also be achieved in XCode by setting:Build Settings -> Optimization Level -> Debug -> Fastest, Smallest, you can change this by writing a simple code, modifying the setting, and then viewing the changes in assembly code

Note 2: hasCustomAWZ()

bool hasCustomAWZ() const {
        return! cache.getBit(FAST_CACHE_HAS_DEFAULT_AWZ); }// Class or superclass has default alloc/allocWithZone: implementation (implementation has no default alloc/allocWithZone)
// Note this is is stored in the metaclass.
#define FAST_CACHE_HAS_DEFAULT_AWZ    (1<<14)
Copy the code
  • Indicates whether the class or parent class has a default alloc/allocWithZone: implementation

Step 4: Enter the _objc_rootAllocWithZone function

id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // allocWithZone under __OBJC2__ ignores the zone parameter
    return _class_createInstanceFromZone(cls, 0.nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}
Copy the code

Step 5: Enter the _class_createInstanceFromZone function, which is the core of alloc

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone,
                              int construct_flags = OBJECT_CONSTRUCT_NONE,
                              bool cxxConstruct = true,
                              size_t *outAllocatedSize = nil)
{
    ASSERT(cls->isRealized());

    // Read class's info bits all at once for performance
    bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor();
    bool hasCxxDtor = cls->hasCxxDtor();
    bool fast = cls->canAllocNonpointer();
    size_t size;
    // Calculate the size of the memory space to be opened
    size = cls->instanceSize(extraBytes);
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else {
        // Request the address pointer from the system according to the memory space
        obj = (id)calloc(1, size);
    }
    if(slowpath(! obj)) {if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if(! zone && fast) {// Associate class with address pointer
        obj->initInstanceIsa(cls, hasCxxDtor);
    } else {
        // Use raw pointer isa on the assumption that they might be
        // doing something weird with the zone or RR.
        obj->initIsa(cls);
    }

    if(fastpath(! hasCxxCtor)) {return obj;
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}
Copy the code

The process is divided into three steps:

  1. instanceSize: Calculates the size of the memory space to be opened
  2. calloc: Applies for an address pointer from the system based on the memory space
  3. initInstanceIsa: Associates classes with addresses

instanceSize: Calculates the memory size

The main process is as follows:

Note 1: Align1616 Byte alignment algorithm

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}
Copy the code
  1. For example,:

  • ~I’m going to take the inverse, which is equal to1 goes to 0,0 goes to 1.& (and)isThe same as 1 is 1, and the other way around is 0
  • word_alignwithalign16The algorithms are similar, except for the formerPlus 7, and then minus 7
  1. Why byte alignment is necessary:
  • All mobile phones over 5s use 64-bit cpus. A 64-bit CPU can process 64-bit data at a time, and 1 byte equals 8bit. In other words, a 64-bit CPU can process 8 bytes of data at a time

  • Apple uses 16-byte alignment, because the first thing in OC’s object is called the ISA pointer, it has to exist, and it takes up 8 bytes, and even if you have no other properties in your object, there must be an ISA, so the object has to take up at least 8 bytes

  • With 8-byte alignment, if you have two contiguous objects that have no attributes, their memory Spaces will be completely next to each other, making them confusing. In 16-byte blocks, this ensures that the CPU can read in blocks more efficiently without being cluttered.

calloc: Applies for an address from the system based on the memory space

obj = (id)calloc(1, size);
Copy the code

If the address is 0x00000001002e3dc5 before calloc, the address is 0x00000001002e3dc5. If the address is 0x00000001002e3dc5 before calloc, the address is 0x00000001002e3dc5.

Usually we print objects of the form

, but here we just have the address. Why? This is mainly because OBj has not passed in a class to associate with

initInstanceIsa: Associates classes with address Pointers

Now that we have the address of the pointer from Calloc, we need to associate the class with the address

  • Enter theinitInstanceIsatheinitIsaFunction, the process is as follows:

  • After executing the process, we print through the breakpointobj:

Conclusion:

  1. By reading and analyzing the source code, we confirmed what we had suspected,allocIt’s all about creating objects
  2. Create it in three steps:To calculate -> To apply for space -> associated

initTo explore the

Class init

// Replaced by CF (throws an NSException)
+ (id)init {
    return (id)self;
}
Copy the code
  • In the sourceinitThe method is to returnself

Instance init

- (id)init {
    return _objc_rootInit(self);
}

id
_objc_rootInit(id obj)
{
    // In practice, it will be hard to rely on this function.
    // Many classes do not properly chain -init calls.
    return obj;
}
Copy the code
  • This method also returnsself, it is aA constructor, you can repair the functionclassI’m going to extend it a little bit, which isThe factory pattern

newTo explore the

+ (id)new {
    return [callAlloc(self.false/*checkNil*/) init];
}
Copy the code
  • In nature andallocIt doesn’t make any difference, except if a class in the project usesinitWrite the constructor while usingnewTo create an object, do not go through this custom construct, which may lead to some errors
  • Compared with theinitMore flexible, recommendedinit, according to the scenarionew