To really see how much memory an OC object takes up, practice is essential.

The initial OC object occupies memory

To create a Command Line Tool project, open main.m and create an NSObject in the main function.

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *objc = [[NSObject alloc] init];
    }
    return 0;
}
Copy the code

Open the terminal /iTerm2 and go to the main.m directory. Convert it to c++ source code.

clang -rewrite-objc main.m -o main.cpp
Copy the code

There is an extra main. CPP file in the folder directory, open it. Don’t panic when you see 98242 lines of code. We just need to focus on NSObject. Search NSObject_IMPL.

struct NSObject_IMPL {
    Class isa;
};
Copy the code

This is the C++ structure for NSOject. It contains a Class pointer. Search found

typedef struct objc_class *Class;
Copy the code

A pointer to the struct objc_class structure type. So what we’ve found so far is that the NSObject object’s corresponding structure contains only one ISA pointer variable, and a pointer variable is 8 bytes in size on a 64-bit machine.

Does that mean that an NSObject takes up 8 bytes of memory? The answer is: all OC objects are at least 16 bytes.

Let’s verify this first. (If you are interested, you can go to the source code of the object created in main. CPP.)

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        NSObject *lbobjc = [[NSObject alloc] init];
        
        NSLog(@" Actual memory size required by lbobJC object: %zd",class_getInstanceSize([lbobjc class]));
        NSLog(@" Actual size of memory allocated for lbobJC object: %zd",malloc_size((__bridge const void *)(lbobjc)));
    }
    return 0;
}
Copy the code

Print the result

Ios-oc object memory usage exploration [2903:181348] LBOBJC object actual memory size: 8

[2903:181348] The actual size of memory allocated by the lbobJC object: 16

Let’s take a look at memory. Print statement with a breakpoint. Leave you.

lldb -> po lbobjc

Method for viewing memory contents:

  • 1️ open memory viewing tool:

Enter the object address 0x1007579C0 in the address box

  • 2 ️ ⃣ LLDB

Both methods indicate that the last few bytes of the object we are creating so far are 00.

We can find out by reading objC4’s source code. If you look at the implementation of the alloc and allocWithZone functions in obj4, you can see that each of these functions calls an instanceSize function:

size_t instanceSize(size_t extraBytes) {
     size_t size = alignedInstanceSize() + extraBytes;
      // CF requires all objects be at least 16bytes.
      if (size < 16) size = 16;
      return size; 
}
Copy the code

The above source code we see the answer, will open up at least 16 bytes. So why use 16 bytes to store 8 bytes of content? Here’s a quick explanation.

In fact, this is mainly related to the hardware problem, because different manufacturers need a set of standardized solutions to solve the different rules between different manufacturers caused by the use of memory is not uniform. Byte alignment was created to solve this problem.

At this point, I also want to look at memory usage when this object contains multiple properties. So that we can understand the memory usage of OC objects once and for all.

Contains memory usage for other attributes

Create an LBPerson class that inherits NSObject and contains three int properties

#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface LBPerson : NSObject
@property (nonatomic.assign) int age;
@property (nonatomic.assign) int height;
@property (nonatomic.assign) int row;
@end
Copy the code

Let’s go back to main

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
#import "LBPerson.h"

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        LBPerson * obj = [[LBPerson alloc] init];
        obj.age = 4;
        obj.height = 5;
        obj.row = 6;
        NSLog(@" Actual memory size required by lbobJC object: %zd",class_getInstanceSize([obj class]));
        NSLog(@" Actual size of memory allocated for lbobJC object: %zd",malloc_size((__bridge const void *)(obj)));
    }
    return 0;
}
Copy the code

Print the result

[3012:201559] Actual memory size required by lBOBJC object: 24

[3012:201559] The actual size of memory allocated by the lbobJC object: 32

LLDB View memory

This is a strange phenomenon, the actual memory size is 24, why? And this is actually how the structure is distributed in vivo.

  • Each member of a structure has an offset relative to the first address of the structure that is an integer multiple of the size of the member, and the compiler adds padding bytes between the members if necessary

  • The total size of the structure is an integer multiple of the size of the widest member of the structure.

  • The first address of a structure variable is divisible by the size of its widest base type member.

  • For compound structures that contain structure variables in their member attributes, children of the compound type member should be included when determining the widest primitive type member. But when you determine the offset position of a compound type member, you look at the compound type as a whole.

Because of the original structureisaPointers take up eight,ageAttributes take up four,heightTake up four,rowAttributes occupy four more, which are not automatically offset because they are divisible. Since the total size of the structure is an integer multiple of the size of the widest member of the structure, and the alignment of the lines meets the 16-byte rule (found in the libmaclloc source code), the actual total footprint is 24. The actual opening satisfies the alignment criterion and opens 32.

The following image shows the libmaclloc source code in nano_malloc.c

Go ahead and change some of the int types to double. You’ll find something new that I won’t cover for space reasons. Direct up result

  • age: int , height : double , row :int

  • age: int , height : Double , row :Double

Summary (consider only64A) :

  • OC objects occupy the least amount of space16One byte of memory.
  • When an object contains attributes, the attributes are allocated memory. Automatic migration and complement under the principle of internal storage and allocation of structure.
  • The object finally satisfies16Byte alignment standards.
  • The property finally satisfies8Byte alignment standards.
  • You can customize the alignment with #pragma Pack ().