NSObject
Class, objc_object, and ID are all defined based on objc_class. An NSObject object is a structure pointer of type objc_Object *
typedef struct objc_class *Class; // the struct pointer to objc_class *, Class // Represents the configuration of an instance of a Class. Struct objc_object {Class _Nonnull ISA OBJC_ISA_AVAILABILITY; }; /// A pointer to an instance of a class. typedef struct objc_object *id; // redefine a name idCopy the code
alloc
When you create an object in ios and you create an NSObject object, you basically use alloc syntax, and you need to access the source code to study it (objC-750 as an example)
I’m going to create an NSObject object
LSPerson *person = [[LSPerson alloc] init]
Copy the code
When debugging with the source code, you can hit alloc to enter the nsobject. mm file, which contains the logic to create an NSObject object
+ (id)alloc {
return _objc_rootAlloc(self);
}
Copy the code
See that the call is actually a C method, then go to _objc_rootAlloc and find that callAlloc is called
id
_objc_rootAlloc(Class cls)
{
return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}
Copy the code
The ALWAYS_INLINE macro is one of the optimizations of LVVM, which optimizes the call to c functions.
Check the class hasCustomAWZ method to see if it overwrites the allocWithZone method. If it can be created quickly (the response object space has already been requested, etc.), initInstanceIsa is initInstanceIsa; otherwise, class_createInstance is class_createInstance. I’m going to create the object, return the object, and I’m going to see what class_createInstance does and the allocWithZone is false by default, so I’m going to call the last allocWithZone when I override the allocWithZone method, okay
static ALWAYS_INLINE id callAlloc(Class cls, bool checkNil, bool allocWithZone=false) { if (slowpath(checkNil && ! cls)) return nil; #if __OBJC2__ if (fastpath(! CLS ->ISA()->hasCustomAWZ())) {// 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 // No shortcuts available. if (allocWithZone) return [cls allocWithZone:nil]; return [cls alloc]; }Copy the code
Enter the class_createInstance method and find that an LVVM-optimized function _class_createInstanceFromZone is called. The main methods are instanceSize, calloc, and initInstanceIsa
id class_createInstance(Class cls, size_t extraBytes) { return _class_createInstanceFromZone(cls, extraBytes, nil); } static __attribute__((always_inline)) id _class_createInstanceFromZone(Class cls, size_t extraBytes, void *zone, bool cxxConstruct = true, size_t *outAllocatedSize = nil) { if (! cls) return nil; assert(cls->isRealized()); // 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; // Use raw pointer isa on the assumption that they might be // doing something weird with the zone or RR. obj->initIsa(cls); } if (cxxConstruct && hasCxxCtor) { obj = _objc_constructOrFree(obj, cls); } return obj; }Copy the code
instanceSize
This method is to calculate the size of the object’s pre-requested space, AlignedInstanceSize, word_align, unalignedInstanceSize are called UnalignedInstanceSize, word_align, unalignedInstanceSize, word_align
UnalignedInstanceSize = ro; unalignedInstanceSize = RO;
Word_align is byte alignment, which is an integer multiple of 8, for example :(12 + 7) & ~7 = 16
Finally, the instanceSize method is used to locate the minimum 16 bits if the requested memory size is less than 16, based on the total number of bytes previously calculated
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; } uint32_t unalignedInstanceSize() { assert(isRealized()); return data()->ro->instanceSize; } uint32_t alignedInstanceSize() { return word_align(unalignedInstanceSize()); Static uint32_t word_align(uint32_t x) {return (x + WORD_MASK) & ~WORD_MASK; }Copy the code
calloc
Calloc allocates the actual memory space for the system, which is actually an integer multiple of aligned 16 bytes. Malloc_size can be used to see how much space this object allocates
void *p = calloc(1, 24);
NSLog(@"%lu",malloc_size(p));
Copy the code
Word_align = 16; word_align = 16; word_align = 16; word_align = 16
#define SHIFT_NANO_QUANTUM 4 #define NANO_REGIME_QUANTA_SIZE (1 << SHIFT_NANO_QUANTUM) // 16 static MALLOC_INLINE size_t segregated_size_to_fit(nanozone_t *nanozone, size_t size, size_t *pKey) { size_t k, slot_bytes; if (0 == size) { size = NANO_REGIME_QUANTA_SIZE; // Historical behavior } k = (size + NANO_REGIME_QUANTA_SIZE - 1) >> SHIFT_NANO_QUANTUM; // round up and shift for number of quanta slot_bytes = k << SHIFT_NANO_QUANTUM; // multiply by power of two quanta size *pKey = k - 1; // Zero-based! return slot_bytes; }Copy the code
initInstanceIsa
This step updates the ISA directly, eventually returning to the outermost layer
inline void objc_object::initInstanceIsa(Class cls, bool hasCxxDtor) { assert(! cls->instancesRequireRawIsa()); assert(hasCxxDtor == cls->hasCxxDtor()); initIsa(cls, true, hasCxxDtor); } inline void objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) { assert(! isTaggedPointer()); if (! nonpointer) { isa.cls = cls; } else { assert(! DisableNonpointerIsa); assert(! cls->instancesRequireRawIsa()); isa_t newisa(0); 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; isa = newisa; }}Copy the code
Where is the value ro->instanceSize from
An object of type objC_Object contains an ISA of type ObjC_Object (Class). Isa can be tested to be 8 bytes in size
Here are the system allocation rules
Isa + int + name + int = 8 + 4 + 8 + 4 = 24, after byte alignment, instanceSize = 24, mallocSize = 32
@interface LSPerson : NSObject
@property (nonatomic, assign) int height;
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) int age;
@end
Copy the code
Note:
Now the system has optimized the parameters to improve the access speed, and now a block of memory is 8 bytes long
Take LSPerson, above
Previous storage: 16 bytes actually occupied
Current storage mode: the actual occupied bytes are 24
InstanceSize = 8 + 8 + 8 + 8 = 32
In fact, the current property location system will be adjusted reasonably from small to large, that is, the property may be height, age, name, so the space size is still 24 and there is no waste phenomenon
init
So, alloc, let’s talk about his partner init, very few words, source code
You can see that either the class method or the object method that you normally call init, it actually returns self, so init doesn’t really do anything, right?
This is actually a design pattern given by Apple that allows developers to override method initial parameters and so on
+ (id)init {
return (id)self;
}
- (id)init {
return _objc_rootInit(self);
}
id
_objc_rootInit(id obj)
{
return obj;
}
Copy the code
new
So just to introduce you, you’ll see some people call a new function, and once you know alloc, the logic is a little bit better, you just call callAlloc to create it, and then you call init, which is like missing the _objc_rootAlloc c function call, and if you didn’t override allocWithZone, So the implementation is exactly the same
+ (id)new {
return [callAlloc(self, false/*checkNil*/) init];
}
Copy the code
allocWithzone
Finally, take a look at allocWithzone, which is also an indirect call to class_createInstance
+ (id)allocWithZone:(struct _NSZone *)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); } id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone) { id obj; (void)zone; obj = class_createInstance(cls, 0); if (slowpath(! obj)) obj = callBadAllocHandler(cls); return obj; }Copy the code