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 fromNSObject
thealloc
Method to explore the process of object creation. From the code call below, it goes tocallAlloc
Methods.
+ (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 incallAlloc
The 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
- 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.
- If not, the value is calculated.
else {
size_t size = alignedInstanceSize() + extraBytes;
}
Copy the code
- 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
- Creates a union bit-field structure
isa_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
- 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