Pre

objc4-824

Ask questions

First we have the following code

YKPerson *p0 = [YKPerson alloc];
YKPerson *p1 = [p0 init];
YKPerson *p2 = [p0 init];

NSLog(@"%@-%p-%p",p0,p0,&p0);
NSLog(@"%@-%p-%p",p1,p1,&p1);
NSLog(@"%@-%p-%p",p2,p2,&p2);
Copy the code

According to the print results, we can see that the memory address space pointing to is the same, but the Pointers are stored on the stack, and are contiguously stored, each 8 bytes apart.

Process analysis

We learn fromNSObjecttheallocMethod to explore the process of object creation. From the code call below, it goes tocallAllocMethods.

+ (id)alloc {
    return _objc_rootAlloc(self);	// self -> YKPerson
}

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

static ALWAYS_INLINE id
callAlloc(Class cls,	// YKPerson
          bool checkNil,	// false
          bool allocWithZone=false) 	// true
{...if (fastpath(! cls->ISA() - >hasCustomAWZ())) {
        return_objc_rootAllocWithZone(cls, nil); }...if (allocWithZone) {
        return ((id(*)(id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil); }...}Copy the code

In fact, during llVM_read_images, the fixupMessageRef function is called, where the alloc call is redirected

if (msg->sel == @selector(alloc)) {
    msg->imp = (IMP)&objc_alloc;
}
Copy the code

So when we call alloc, we’re actually calling objc_alloc

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

callAlloc

In the above judgment we can comb through incallAllocThe flow of judgment in a method.

Judge conditions

fastpath(! cls->ISA() - >hasCustomAWZ())
Copy the code

The result is true

As you can see from the call flow of the code, it goes to the _objc_rootAllocWithZone method

+ (id)allocWithZone:(struct _NSZone *)zone {
    return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone);
}
Copy the code

The result is false

As you can see from the call flow of the code, it goes to the _objc_rootAllocWithZone method

return _objc_rootAllocWithZone(cls, nil);

id
_objc_rootAllocWithZone(Class cls, malloc_zone_t *zone __unused)
{
    // See the detailed analysis below
    return _class_createInstanceFromZone(cls, 0, nil,
                                         OBJECT_CONSTRUCT_CALL_BADALLOC);
}
Copy the code

【 Core method 】_class_createInstanceFromZone

static ALWAYS_INLINE id
_class_createInstanceFromZone(Class cls,	// YKPerson
                              size_t extraBytes,	/ / 0
                              void *zone,	// nil
                              int construct_flags = OBJECT_CONSTRUCT_NONE,	// OBJECT_CONSTRUCT_CALL_BADALLOC
                              bool cxxConstruct = true.size_t *outAllocatedSize = nil)
Copy the code

Basic condition judgment

ASSERT(cls->isRealized());
bool hasCxxCtor = cxxConstruct && cls->hasCxxCtor(a);// whether there is a C++ constructor
bool hasCxxDtor = cls->hasCxxDtor(a);// whether there is a C++ destructor
bool fast = cls->canAllocNonpointer(a);// Whether it can be allocated
Copy the code

Gets the memory size required by an instance of CLS

size_t size;
size = cls->instanceSize(extraBytes); // extraBytes == 0
Copy the code
  1. If the instance size has been obtained previously, the value is returned from the cache.
    if (fastpath(cache.hasFastInstanceSize(extraBytes))) {
        return cache.fastInstanceSize(extraBytes);
    }
Copy the code

__builtin_constant_p(extra) && extra == 0

/*
* true
*/
return _flags & FAST_CACHE_ALLOC_MASK16;

/*
* false
*/
size_t size = _flags & FAST_CACHE_ALLOC_MASK; // #define FAST_CACHE_ALLOC_MASK 0x1ff8
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16); // #define FAST_CACHE_ALLOC_DELTA16 0x0008 align16() is 16 bytes aligned
Copy the code

As for __builtin_constant_p, it is a compiler built-in function. It takes a numeric argument and returns 1 if the argument is known to be a compile-time constant. The return value of 0 means that the compiler cannot determine whether the parameter is a compile-time constant. The typical use of this built-in function is in macros for manual compile-time optimization.

  1. If not, the value is calculated.
else {
  size_t size = alignedInstanceSize() + extraBytes;
}
Copy the code
  1. If the size is less than 16, 16 bytes are added according to the principle of “Memory replenishment”
// CF requires all objects be at least 16 bytes.
if (size < 16) size = 16;
return size;
Copy the code

Open up memory space and return address pointer

id obj;
obj = (id)calloc(1, size);

// ->
void	*calloc(size_t __count, size_t __size) __result_use_check __alloc_size(1,2);
Copy the code

Bind the memory address to the class

obj->initInstanceIsa(cls, hasCxxDtor);

// ->
initIsa(cls, false.false);
Copy the code
inline void 
objc_object::initIsa(Class cls,	// YKPerson
                     bool nonpointer,	// false
                     bool hasCxxDtor)	// false
{
    isa_t newisa(0)... newisa.setClass(cls, this); ... the isa = newisa; }Copy the code
  1. Creates a union bit-field structureisa_t
union isa_t {
    isa_t() {}isa_t(uintptr_t value) : bits(value) { }
    uintptr_t bits;

private:
    Class cls;

public:

#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };

    bool isDeallocating(a) {
        return extra_rc == 0 && has_sidetable_rc == 0;
    }
    
    void setDeallocating(a) {
        extra_rc = 0;
        has_sidetable_rc = 0;
    }
#endif
    void setClass(Class cls, objc_object *obj);
    Class getClass(* *bool** authenticated);
    Class getDecodedClass(* *bool** authenticated);
};
Copy the code
  1. The binding
inline void
isa_t::setClass(Class newCls, UNUSED_WITHOUT_PTRAUTH objc_object *obj)
{
#if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
#   if ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_NONE

    uintptr_t signedCls = (uintptr_t)newCls;

#   elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ONLY_SWIFT

    uintptr_t signedCls = (uintptr_t)newCls;
    if (newCls->isSwiftStable())
        signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));

#   elif ISA_SIGNING_SIGN_MODE == ISA_SIGNING_SIGN_ALL

    uintptr_t signedCls = (uintptr_t)ptrauth_sign_unauthenticated((void *)newCls, ISA_SIGNING_KEY, ptrauth_blend_discriminator(obj, ISA_SIGNING_DISCRIMINATOR));

#   else
#       error Unknown isa signing mode.
#   endif

    shiftcls_and_sig = signedCls >> 3;

#elif SUPPORT_INDEXED_ISA
    cls = newCls;

#else
    shiftcls = (uintptr_t)newCls >> 3;
#endif
}
Copy the code

supplement

Memory alignment