Objc4 source code is based on version 779.1. The article is purely personal learning views, fallacies, hope to correct.
alloc
Method call stack
-
- (id)alloc
- _objc_rootAlloc()
- callAlloc()
- _objc_rootAllocWithZone()
- _class_createInstanceFromZone()
- cls->instanceSize()
- malloc_zone_calloc | calloc
- obj->initInstanceIsa()
- obj->initIsa()
- object_cxxConstructFromClass()
- _class_createInstanceFromZone()
- _objc_rootAllocWithZone()
- callAlloc()
- _objc_rootAlloc()
- (id)alloc
Source code details
NSObject.mm
– line:2317
+ (id)alloc {
return _objc_rootAlloc(self);
}
Copy the code
NSObject.mm
– line:1718
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
Copy the code
NSObject.mm
– line:1697
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, Bool allocWithZone=false) {// Objective-C 2.0 #if __OBJC2__ // Void if (slowPath (checkNil &&! cls)) return nil; // Whether this class or superclass implements alloc/allocWithZone. If not, the metaclass 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
objc-runtime-new.mm
– line:7440
NEVER_INLINE
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
objc-runtime-new.mm
– line:7386
static ALWAYS_INLINE id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, Int construct_flags = object_none, bool cxxConstruct = true, size_t *outAllocatedSize = nil) {// The class object itself is not implemented, Then terminate ASSERT(CLS ->isRealized()); // Read class's info bits all at once for performance bool hasCxxCtor = cxxctor && CLS ->hasCxxCtor(); Bool hasCxxDtor = CLS ->hasCxxDtor(); Bool fast = CLS ->canAllocNonpointer(); size_t size; Size = CLS ->instanceSize(extraBytes); if (outAllocatedSize) *outAllocatedSize = size; id obj; If (zone) {obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size); } else {obj = (id)calloc(1, size); } // Assign failed if (slowpath(! Obj)) {if (construct_flags & object_callt_badalloc) {return _objc_callBadAllocHandler(CLS); } return nil; } // initialize isa_t if (! Zone && fast) {// Initialize the optimization 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. // Initialize the plain pointer obj->initIsa(CLS); } // No constructor, then return the object if (fastPath (! hasCxxCtor)) { return obj; } construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE; Return object_cxxConstructFromClass(obj, CLS, construct_flags); return object_cxxFromClass (obj, CLS, construct_flags); }Copy the code
objc-runtime-new.h
– line:1497
size_t instanceSize(size_t extraBytes) const { if (fastpath(cache.hasFastInstanceSize(extraBytes))) { return cache.fastInstanceSize(extraBytes); Size_t size = alignedInstanceSize() + extraBytes; // CF requires all objects to be at least 16 bytes. // Minimum 16 bytes if (size < 16) size = 16; return size; }Copy the code
objc-object.h
– line:215
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { ASSERT(! cls->instancesRequireRawIsa()); ASSERT(hasCxxDtor == cls->hasCxxDtor()); // Initialize isa pointer initIsa(CLS, true, hasCxxDtor); }Copy the code
objc-object.h
– line:224
inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { ASSERT(! isTaggedPointer()); // nonpointer false: normal pointer true: optimized pointer if (! // initialize the common pointer isa = isa_t((uintptr_t) CLS); } else { ASSERT(! DisableNonpointerIsa); ASSERT(! cls->instancesRequireRawIsa()); // initialize the optimization pointer isa_t newisa(0); #if SUPPORT_INDEXED_ISA ASSERT(CLS ->classArrayIndex() > 0); newisa.bits = ISA_INDEX_MAGIC_VALUE; // isa.magic is part of ISA_MAGIC_VALUE // isa.nonpointer is part of ISA_MAGIC_VALUE newisa.has_cxx_dtor = hasCxxDtor; newisa.indexcls = (uintptr_t)cls->classArrayIndex(); #else newisa.bits = ISA_MAGIC_VALUE; // isa.magic is part of ISA_MAGIC_VALUE // isa.nonpointer is part of ISA_MAGIC_VALUE newisa.has_cxx_dtor = hasCxxDtor; newisa.shiftcls = (uintptr_t)cls >> 3; #endif // This write must be performed in a single store in some cases // (for example when realizing a class because other threads // may simultaneously try to use the class). // fixme use atomics here to guarantee single-store and to // guarantee memory order w.r.t. the class index table // ... but not too atomic because we don't want to hurt instantiation isa = newisa; }}Copy the code
objc-class.mm
– line:487
id object_cxxConstructFromClass(id obj, Class cls, int flags) { ASSERT(cls->hasCxxCtor()); // required for performance, not correctness id (*ctor)(id); Class supercls; supercls = cls->superclass; // Call superclasses' ctors first, If (supercls && supercls->hasCxxCtor()) {// Call the parent constructor recursively bool OK = object_cxxConstructFromClass(obj, supercls, flags); // Failure returns nil if (slowpath(! ok)) return nil; // some superclass's ctor failed - give up } // Find this class's ctor, If any. / / call the current class of SEL_cxx_construct method ctor = (id (*) (id)) lookupMethodInClassAndLoadCache (CLS, SEL_cxx_construct); If (ctor == (id(*)(id))_objc_msgForward_impcache) return obj; // no ctor - ok // Call this class's ctor. if (PrintCxxCtors) { _objc_inform("CXX: calling C++ constructors for class %s", cls->nameForLogging()); If (fastPath ((*ctor)(obj))) return obj; // ctor called and succeeded - ok supercls = cls->superclass; // this reload avoids a spill on the stack // This class's ctor was called and failed. // Call superclasses's dtors to If (supercls) object_cxxDestructFromClass(obj, supercls); if (flags & OBJECT_CONSTRUCT_FREE_ONFAILURE) free(obj); if (flags & OBJECT_CONSTRUCT_CALL_BADALLOC) { return _objc_callBadAllocHandler(cls); } return nil; }Copy the code
init
NSObject.mm
line:2331
- (id)init {
return _objc_rootInit(self);
}
Copy the code
NSObject.mm
line:1830
id _objc_rootInit(id obj) { // In practice, It will be hard to rely on this function. // Many classes do not properchain-init calls. Return obj; }Copy the code
new
NSObject.mm
line:2245
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
Copy the code
There’s no difference between new and alloc+init, right
Two macro definitions are common in source code
#define fastpath(x) (__builtin_expect(bool(x), 1))
#define slowpath(x) (__builtin_expect(bool(x), 0))
Copy the code
__builtin_expect(EXP,N) is a directive provided by GCC. This means that the value of x in fastpath is most likely true and the value of x in slowpath is most likely false.
Use this instruction to optimize the compiler’s code layout at compile time and reduce the performance cost caused by instruction jumps.
Malloc is different from calloc
Both dynamically allocate memory. The main difference is that malloc does not initialize allocated memory, which can be arbitrary. Calloc initializes allocated memory to 0.