This paper is the nature of notes, mainly for their own understanding of the place is not very thorough record.

Recommend system directly learn small code brother iOS underlying principle class –MJ teacher’s class is really good, push a wave.


OC Object nature

Based on C and C++ structure


How OC is compiled by the compiler:

OC ==> C++ ==> assembly ==> machine language

In C++, only structs can hold different types of content (such as different attributes).


Convert Objective-C code to C\C++ code

  1. Clang-rewrite-objc OC source file -o output CPP file

Convert the source file to a generic CPP file

  1. Xcrun -sdk iphoneOS clang-arch arm64 -rewrite-objc OC source file -o Output CPP file

Xcode converts the source file to an iphoneos file with arm64 architecture, with less content than the first one

  1. If you need to link to other frameworks, use the -framework argument. For example – framework UIKit

OC and C++ definition of NSObject

  • Definition in OC
@interface NSObject <NSObject> {
    Class isa;
}
Copy the code
  • C++ definition
struct NSObject_IMPL {
	Class isa;
};
Copy the code

For structures, it’s the same as for arrays. The address of its first member is the address of the structure object. So the address of an OC object is actually the address of its ISA pointer.

This isa isa pointer to the objc_class structure

// typedef struct objc_class *Class;Copy the code

A pointer takes up 8 bytes of memory on a 64-bit system

So an OC object takes up at least 8 bytes of memory


NSObject The size of the memory used by the object

The above conclusion is also supported by the class_getInstanceSize function:

#import <objc/runtime.h>/* Get the size of the 'member variables' of the NSObject instance object >> 8 */ NSLog(@"%zd", class_getInstanceSize([NSObject class])); Size_t class_getInstanceSize(Class CLS) {if(! cls)return 0;
    return cls->alignedInstanceSize();
}

// Class's ivar size rounded up to a pointer-size boundary. uint32_t alignedInstanceSize() { return word_align(unalignedInstanceSize()); }Copy the code

Note that word_align returns the aligned size of memory, taking the unalignedInstanceSize size as an argument.

For the NSObject *obj pointer, we have another function that looks at how much memory it’s actually allocated

#import <malloc/malloc.h>>> 16 NSLog(@)"%zd", malloc_size((__bridge const void *)obj));
Copy the code

Why would an 8-byte structure be allocated 16 bytes

Continue to look at runtime

+ (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(slowpath(checkNil && ! cls))return nil;

#if __OBJC2__
    if(fastpath(! cls->ISA()->hasCustomAWZ())) { // No alloc/allocWithZone implementation. Go straight to the allocator. // fixme store hasCustomAWZin 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]; } // Replaced by ObjectAlloc + (id)allocWithZone:(struct _NSZone *)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); } id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone) { id obj; #if __OBJC2__ // allocWithZone under __OBJC2__ ignores the zone parameter (void)zone; obj = class_createInstance(cls, 0); #else if (! zone) { obj = class_createInstance(cls, 0); } else { obj = class_createInstanceFromZone(cls, 0, zone); } #endif if (slowpath(! obj)) obj = callBadAllocHandler(cls); return obj; } 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;
}


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;
}
Copy the code

The alloc function ends up depending oninstanceSizeThe returnedsizeAnd then usecalloc(1, size);Function to allocate memory.

In instanceSize, the alignedInstanceSize method is set to the size of the member variables (already posted above). The extraBytes parameter (as far as I can see) is 0.

whileCoreFoundationIn the framework ofinstanceSizeMemory addresses that are specified to be less than 16 bytes will be supplemented with 16 bytes.

But actually,NSObjectObject is only used8 bytesUsed to storeisaPointer to the


The nature of the Student object

@interface Student : NSObject
{
    @public
    int _no;
    int _age;
}
@end
Copy the code

When I rewrite it in C++

struct Student_IMPL { struct NSObject_IMPL NSObject_IVARS; int _no; int _age; }; struct NSObject_IMPL { Class isa; }; Struct Student_IMPL {Class isa; //8 bytes int _no; //4 bytes int _age; / / 4 bytes};Copy the code

So the essence of an OC object is actually a structure that contains all the parent member variables plus its own member variables


Student memory layout and size

You can check the structure of the specified address by using Debug->Debug Workflow ->View momory

We can also determine the size of the Student instance by using the malloc_size function.

That’s 16. Isa pointer to 8-byte parent class, 4-byte _age int, 4-byte _no int.

The value of the member variable _no can be changed directly in memory by using memory write (stU address +8 offset) 8.


OC object memory allocation under the memory alignment principle

AlignedInstanceSize () function memory alignment

The alignedInstanceSize() function aligns all member variables with the one with the longest memory. Such as

@interface Animal: NSObject
{
    int weight;
    int height;
    int age;
}
Copy the code

In fact, we only need 8+4+4+4=20 bytes, but memory will return 8*3=24

Memory alignment of malloc()/calloc() functions

The size returned by alignedInstanceSize() is used as a reference when the object is actually created. Calloc then actually allocates memory based on buckets for memory alignment. This bucket is an integer multiple of 16.

#define NANO_MAX_SIZE 256 /* Buckets sized {16, 32, 48, 64, 80, 96, 112, ... } * /
Copy the code

So Animal instance objects are actually allocated 32 bytes of memory.


SizeOf and class_getInstanceSize

Returns the size of memory occupied by a parameter object

sizeOf

SizeOf is an operator that is replaced at compile time with a constant value of the memory used by the parameter type.

Because of substitution at compile time, the following features are available:

MJPerson *p = [[MJPerson alloc] init];
NSLog(@"%zd", sizeof(p)); / / 8Copy the code

P will be treated as a pointer at compile time, returning 8 bytes of pointer memory. Not the memory length of type MJPerson.

class_getInstanceSize

Class_getInstanceSize is a method that will be evaluated during program run time.

It can calculate how much memory a class needs at run time

class_getInstanceSize([p class]) //24
Copy the code

objc_class

runtime.h

Class structures prior to OC2.0. After 2.0 only the header file is left and has been marked as OBJC2_UNAVAILABLE deprecated.

struct objc_class {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;

#if ! __OBJC2__
    Class _Nullable super_class                              OBJC2_UNAVAILABLE;
    const char * _Nonnull name                               OBJC2_UNAVAILABLE;
    long version                                             OBJC2_UNAVAILABLE;
    long info                                                OBJC2_UNAVAILABLE;
    long instance_size                                       OBJC2_UNAVAILABLE;
    struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
#endif

} OBJC2_UNAVAILABLE;
Copy the code

objc_runtime_new.h

In the latest Runtime source code, the class structure is optimized and the internal division of labor is clearer.

In the primary structure, only three common members isa, superclass and cache are reserved

The rest of the information is transferred to the class_data_bits_t secondary structure

struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // Method cache class_data_bits_t bits; // The specific class information class_rw_t *data() { 
        return bits.data();
    }
    void setData(class_rw_t *newData) { bits.setData(newData); }... }Copy the code

Inside class_datA_bits_t (the list of class information), there is also class_rw_T (the list of read and write information), which can be modified dynamically

struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint32_t version; const class_ro_t *ro; method_array_t methods; // method property_array_t properties; // Attribute protocol_array_t protocols; // protocol Class firstSubclass; Class nextSiblingClass; char *demangledName; }Copy the code

Inside class_rw_T (list of read and write information), there is also class_ro_T (list of immutable information), which holds the information that the class needs to determine when it is loaded into memory

struct class_ro_t { uint32_t flags; uint32_t instanceStart; uint32_t instanceSize; // The size of memory occupied by the instance object#ifdef __LP64__
    uint32_t reserved;
#endifconst uint8_t * ivarLayout; const char * name; // Class name method_list_t * baseMethodList; protocol_list_t * baseProtocols; const ivar_list_t * ivars; // Uint8_t * weakIvarLayout; property_list_t *baseProperties; method_list_t *baseMethods() const {returnbaseMethodList; }};Copy the code

The resources

Small code brother iOS basic principle class

OC object memory size problem