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 inalloc
In memory, andinit
The operation doesn’t. So whether that’s true or not, we’ll see
Tools to prepare
- Download objC4-818.2 source code, compile can refer to this man’s article source code compilation debugging
- 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 trueslowpath
和fastpath
Is 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:
instanceSize
: Calculates the size of the memory space to be openedcalloc
: Applies for an address pointer from the system based on the memory spaceinitInstanceIsa
: 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
- 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_align
withalign16
The algorithms are similar, except for the formerPlus 7, and then minus 7
- 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 the
initInstanceIsa
theinitIsa
Function, the process is as follows:
- After executing the process, we print through the breakpoint
obj
:
Conclusion:
- By reading and analyzing the source code, we confirmed what we had suspected,
alloc
It’s all about creating objects - Create it in three steps:
To calculate
->To apply for space
->associated
init
To explore the
Class init
// Replaced by CF (throws an NSException)
+ (id)init {
return (id)self;
}
Copy the code
- In the source
init
The 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 returns
self
, it is aA constructor
, you can repair the functionclass
I’m going to extend it a little bit, which isThe factory pattern
new
To explore the
+ (id)new {
return [callAlloc(self.false/*checkNil*/) init];
}
Copy the code
- In nature and
alloc
It doesn’t make any difference, except if a class in the project usesinit
Write the constructor while usingnew
To create an object, do not go through this custom construct, which may lead to some errors - Compared with the
init
More flexible, recommendedinit
, according to the scenarionew