mirror of
https://github.com/NohamR/knowledge-kit.git
synced 2026-05-25 12:27:15 +00:00
159 lines
4.6 KiB
Markdown
159 lines
4.6 KiB
Markdown
# 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 |