# Objective-C Under the Hood 1. Objects and classes in Objective-C are primarily implemented using structs from C/C++. Method 1: You can verify with clang. `clang -rewrite-objc main.m -o main.cpp` To target a specific platform: `xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main-arm64.cpp` ``` struct NSObject_IMPL { Class isa; }; /// An opaque type that represents an Objective-C class. typedef struct objc_class *Class; ``` So Class is a pointer to a struct, which occupies 8 bytes on a 64-bit system and 4 bytes on a 32-bit system. NSObject as a struct therefore occupies 8 bytes. [images omitted] You notice `class_getInstanceSize` and `malloc_size` results differ. ```c // Class's ivar size rounded up to a pointer-size boundary. uint32_t alignedInstanceSize() const { return word_align(unalignedInstanceSize()); } ``` `class_getInstanceSize` returns the size of a class instance's member variables (rounded up to pointer-size boundary, so not the exact allocated size). ```c extern size_t malloc_size(const void *ptr); /* Returns size of given ptr */ ``` `malloc_size` returns the actual size allocated for the pointer. [image omitted] Conclusions: - If a class inherits from NSObject and has no additional properties, the class occupies 16 bytes. `class_getInstanceSize` returns 8, `malloc_size` returns 16. - If a class inherits from NSObject and has additional properties, the class occupies 16 bytes. `class_getInstanceSize` returns 16, `malloc_size` returns 16. Method 2: Verify from source code (top-down) ```c++ // NSObject.mm // Replaced by ObjectAlloc + (id)allocWithZone:(struct _NSZone *)zone { return _objc_rootAllocWithZone(self, (malloc_zone_t *)zone); } // objc-class-old.mm id _objc_rootAllocWithZone(Class cls, malloc_zone_t *zone) { id obj; if (fastpath(!zone)) { obj = class_createInstance(cls, 0); } else { obj = class_createInstanceFromZone(cls, 0, zone); } if (slowpath(!obj)) obj = _objc_callBadAllocHandler(cls); return obj; } // objc-class-old.mm /*********************************************************************** * _class_createInstance. Allocate an instance of the specified * class with the specified number of bytes for indexed variables, in * the default zone, using _class_createInstanceFromZone. **********************************************************************/ static id _class_createInstance(Class cls, size_t extraBytes) { return _class_createInstanceFromZone (cls, extraBytes, nil); } 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); if (outAllocatedSize) *outAllocatedSize = size; id obj; if (zone) { obj = (id)malloc_zone_calloc((malloc_zone_t *)zone, 1, size); } else { obj = (id)calloc(1, size); } if (slowpath(!obj)) { if (construct_flags & OBJECT_CONSTRUCT_CALL_BADALLOC) { return _objc_callBadAllocHandler(cls); } return nil; } if (!zone && fast) { 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); } ``` ```c++ // objc-runtime-new.h // Class's ivar size rounded up to a pointer-size boundary. uint32_t alignedInstanceSize() const { return word_align(unalignedInstanceSize()); } ``` ```c++ // objc-runtime-new.h 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. if (size < 16) size = 16; return size; } ``` "CF requires all objects be at least 16 bytes." The system allocates at least 16 bytes for NSObject objects, but it uses 8 bytes for storing ivars (on 64-bit systems). 2. How memory is allocated for a class that inherits from NSObject