Summary of ios low-level articles

1. The introduction of

Create the object LGPerson class and code it as follows in ViewDidLoad

     <LGPerson: 0x6000006543c0> - 0x6000006543c0 - 0x7ffee47290e8
     <LGPerson: 0x6000006543c0> - 0x6000006543c0 - 0x7ffee47290e0
     <LGPerson: 0x6000006543c0> - 0x6000006543c0 - 0x7ffee47290d8
     
     */
    LGPerson *p1 = [LGPerson alloc];
    LGPerson *p2 = [p1 init];
    LGPerson *p3 = [p1 init];
    LGNSLog(@"%@ - %p - %p",p1,p1,&p1);
    LGNSLog(@"%@ - %p - %p",p2,p2,&p2);
    LGNSLog(@"%@ - %p - %p",p3,p3,&p3);
Copy the code

From the result of debugging terminal, we can know that the addresses of storage P1, P2 and P3 are different, but P1, P2 and P3 point to the same memory space, the stack memory is continuous, and the addresses of P1, P2 and P3 differ by 8 bytes (64-bit system, pointer storage needs 8 bytes).

Think: What is the underlying alloc of the object doing?

2. Three ways to explore the underlying principles

2.1 Method a sign breakpoint form directly with the flow

  • Add symbol breakpoint alloc
  • LGPerson *p1 = [LGPerson alloc]; Add a normal breakpoint before
  • When you run the code, you first break the alloc break, which is because the system is allocating a lot of objects, you set the alloc break point to not enter, and then you run at full speed until LGPerson *p1 = [LGPerson alloc]; Break on alloc, open the break on alloc, and run at full speed
  • Go to the alloc break point of [LGPerson alloc] and find that because LGPerson does not implement alloc method, it is the alloc method of the parent class, libobjc.a. dylib’ +[NSObject alloc]

  • Step into to go to libobjc.a. dylib’_objc_rootAlloc

2.2 Method 2: Press control-step into

  • LGPerson *p1 = [LGPerson alloc]; Add a normal breakpoint before
  • When the break point is broken, press Control-step into

  • Notation breakpoint objc_alloc, look at the source
  • Step into to enter _objc_rootAlloc

2.3 Method 3 Compilation View and follow process

  • Set Debug -> Debug Workflow -> Always Show Disassembly

  • Control +step into into objc_alloc
  • Set the symbol breakpoint objc_alloc and look at the source
  • Step into to enter _objc_rootAlloc

3. View the alloc source code in the obj781 source project

3.1 Apple Open Source

https://opensource.apple.com
https://opensource.apple.com/tarballs
Copy the code

Find Source in nsobject. mm +(id)alloc implementation global search alloc {locate in nsobject. mm file

+ (id)alloc {
    return _objc_rootAlloc(self);
}

id _objc_rootAlloc(Class cls)
{
    return callAlloc(cls,false/*checkNil*/.true/*allocWithZone*/);
}

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())) {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));
}

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

The call chain is: [[HTPerson alloc]init] –> objc_alloc_init –> callAlloc –> ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)) –> +alloc –> _objc_rootAlloc –> callAlloc –>objc_rootAllocWithZone –> _class_createInstanceFromZone

[HTPerson alloc]–>objc_alloc–>callAlloc –> ((id(*)(id, SEL))objc_msgSend)(cls, @selector(alloc)) –> +alloc –> _objc_rootAlloc –> callAlloc –>objc_rootAllocWithZone –> _class_createInstanceFromZone

After entering the _class_createInstanceFromZone branch processing, simply from the source code can not determine how the process –>

  • LGPerson *p1 = [LGPerson alloc]; Add a normal breakpoint before
  • After execution to the breakpoint, add the symbol breakpoint callAlloc
  • Step through tracing the program flow

Register read after breakpoint

register read x0 ; X0 is where the first argument is stored when the function is called, and the return value is stored when the function returns

  • How much memory allocated: 8 bytes aligned, minimum 16 bytes

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 be at least 16 bytes. Minimum 16 bytes
        if (size < 16) size = 16;
        return size;
    }
    
    size_t fastInstanceSize(size_t extra) const
    {
        ASSERT(hasFastInstanceSize(extra));
        if (__builtin_constant_p(extra) && extra == 0) {
            return _flags & FAST_CACHE_ALLOC_MASK16;
        } else {
            size_t size = _flags & FAST_CACHE_ALLOC_MASK;
            // remove the FAST_CACHE_ALLOC_DELTA16 that was added
            // by setFastInstanceSize FAST_CACHE_ALLOC_DELTA16 = 0x0008
            returnalign16(size + extra - FAST_CACHE_ALLOC_DELTA16); }}// 16-byte alignment, apple's early 8-byte alignment
  static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}        
Copy the code

NSObject *objc1 = [[NSObject alloc] init];

At this point, alloc does not go through the above process. After viewing the assembly, it is found that alloc calls objc_alloc when OC goes to assembly

When I call alloc, when do I go to objc_alloc? – > LLVM source to view the message forwarding function GeneratePossiblySpecializedMessageSend – > if judgement conditions tryGenerateSpecializedMessageSend call — – > “alloc “News, call EmitObjCAlloc – > object forward in the message alloc, forward to objc_alloc message – > GeneratePossiblySpecializedMessageSend function the if judgment

When GeneratePossiblySpecializedMessageSend function the if judgment tryGenerateSpecializedMessageSend returns false – > forward the original message “alloc” : GenerateMessa GeSend, EG: the calling process of [HTPerson alloc]

If judgment tryGenerateSpecializedMessageSend returns true, to get into the if branch to return to, no longer for message forwarding, eg: [NSObject alloc] the calling process

3.3 Byte alignment of object memory allocation

IOS uses 16-byte aligned memory allocation. When allocating memory for objects, addresses need to be allocated are: ISA, an object attribute, and the actual required address is passed into function align16 to conduct 16-byte alignment and return

static inline size_t align16(size_t x) {
    return (x + size_t(15)) & ~size_t(15);
}
Copy the code

3.4 Setting compiler optimization

4. alloc &init & new

4.1 alloc

  • Allocating memory
  • Initialize the isa

4.2 the init

   // Replaced by CF (throws an NSException)

+ (id)init {
    return (id)self;
}

- (id)init {
    return _objc_rootInit(self);
} 
Copy the code

Init is a constructor, factory design pattern, that provides an entry point for developers to customize subclasses

- (instancetype)init{
    self = [super init];
    if(self){
        [selfsetupUI]; }}Copy the code

4.3 new

+ (id)new {
    return [callAlloc(self.false/*checkNil*/) init];
}
Copy the code

Equivalent to alloc+init, but new is not recommended. If an object child defines an initialization function initWithxxx, the overwritten initialization of initWithxxx: is not called

5. Assign object addresses and attribute addresses

5.1 mask

5.2 Isa and property addresses of objects

X /4gx x: print in hexadecimal format. 4G: print four segments.