OC object-oriented programming, we often use alloc, init, new to instantiate classes into objects, so how they work, and what is the relationship between them?

One, let’s look at some code

NSObject *objc1 = [NSObject alloc];
NSObject *objc2 = [objc1 init];
NSObject *objc3 = [objc1 init];
NSLog(@"%@ - %p",objc1,&objc1);
NSLog(@"%@ - %p",objc2,&objc2);
NSLog(@"%@ - %p",objc3,&objc3);
Copy the code

Output:

InitViewController.m:(23).NSObject: 0x600000a94be0> - 0x7ffee89358b8
InitViewController.m:(24).NSObject: 0x600000a94be0> - 0x7ffee89358b0
InitViewController.m:(25).NSObject: 0x600000a94be0> - 0x7ffee89358a8
Copy the code

Objc1, objc2, and objC3 are represented by different Pointers to the same memory address.

Why did this happen? The reason for this is in the code:

Click Command + jump to alloc to find nsobjct.h

+ (instancetype)alloc OBJC_SWIFT_UNAVAILABLE("use object initializers instead");
Copy the code

So that’s the end of it, so where do we look at how alloc is implemented

Two, three kinds of breakpoint

There are three ways to see the source code in the library

Method 1:control + step into
objc_alloc: -> 
libobjc.A.dylib`objc_alloc: -> 
libobjc.A.dylib`_objc_rootAllocWithZone: -> 
libsystem_malloc.dylib`calloc: -> ...
Copy the code
Method 2: Symbolic breakpoints

Location: Debug->BreakPoints->Create Symbolic Breakpoint

The symbol alloc appears libobjc.a.dylib +[NSObject alloc]:

Method 3: Look at the assembly

Location: Debug->Debug Workflow->Always show Disassembly

Breakpoint, then go to assembly control + step in will go to libobjc.a.dylib objc_alloc:

All three of the above methods point to the same library libobjc.a.dylib. Let’s examine a library

Third, source code analysis

Objc source code address

How to compile this source code was written in the previous blog objC4-818.2 source code compilation

3.1.0 Alloc implementation principle

MMObject class doesn’t implement anything, right

#import <Foundation/Foundation.h>
#import "MMObject.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        MMObject *objc = [MMObject alloc];
    }
    return 0;
}
Copy the code
+ (id)alloc {
    return _objc_rootAlloc(self);
}
Copy the code
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/.true/*allocWithZone*/);
}
Copy the code
static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if(slowpath(checkNil && ! cls))return nil;
    if(fastpath(! cls->ISA()->hasCustomAWZ())) {// +allocWithZone is not implemented
        return _objc_rootAllocWithZone(cls, nil);/ / go here
    }
#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
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
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;

    size = cls->instanceSize(extraBytes);// Calculate the size of memory to be opened
    if (outAllocatedSize) *outAllocatedSize = size;

    id obj;
    if (zone) {
        obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size);
    } else{go here obj = (id)calloc(1, size);// Request memory
    }
    if(slowpath(! obj)) {if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) {
            return _objc_callBadAllocHandler(cls);
        }
        return nil;
    }

    if(! zone && fast) {/ / this
        obj->initInstanceIsa(cls, hasCxxDtor);// Associate the class with isa
    } 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)) {/ / this
        return obj;// Return this object
    }

    construct_flags |= OBJECT_CONSTRUCT_FREE_ONFAILURE;
    return object_cxxConstructFromClass(obj, cls, construct_flags);
}
Copy the code

Alloc -> _objc_rootAlloc -> callAlloc -> hasCustomAWZ() not implemented +allocWithZone -> _objc_rootAllocWithZone -> _class_createInstanceFromZone allocates memory inside calloc associates ISA with class initInstanceIsa returns objC

3.1.1 MMObjectClass+allocWithZone
#import "MMObject.h"
@implementation MMObject

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    static id instance = nil;
    @synchronized (self) {
        if (instance == nil) {
            instance = [superallocWithZone:zone]; }}return instance;
}
@end
Copy the code

The preceding is the same, hasCustomAWZ() will skip it

static ALWAYS_INLINE id
callAlloc(Class cls, bool checkNil, bool allocWithZone=false)
{
#if __OBJC2__
    if(slowpath(checkNil && ! cls))return nil;
    if(fastpath(! cls->ISA()->hasCustomAWZ())) {// Does not include custom +allocWithZone
        return _objc_rootAllocWithZone(cls, nil);
    }
#endif

    // No Shortcuts available
    if (allocWithZone) {// Go here because allocWithZone is implemented
        return ((id(*) (id, SEL, struct _NSZone *))objc_msgSend)(cls, @selector(allocWithZone:), nil);
    }
    return ((id(*) (id, SEL))objc_msgSend)(cls, @selector(alloc));
}
Copy the code

3.2 initImplementation principle of
MMObject *objc1 = [objc init];
Copy the code
- (id)init {
    return _objc_rootInit(self);
}
Copy the code
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

Init we can see it doesn’t do anything, it just returns an obj, so what’s the use of this? It’s a factory design pattern, so we can do some other things when we initialize it.

3.2 newImplementation principle of
MMObject *objc2 = [MMObject new];
Copy the code
+ (id)new {
    return [callAlloc(self.false/*checkNil*/) init];
}
Copy the code

New calls callAlloc with default false allocWithZone=false and calls init by default.

conclusion

Alloc opens up memory and associates the class with isa Pointers

Init does nothing, returns the principle class, right

[Class new] is equivalent to [[Class alloc]init.