A list,

Before Swift, iOS development used Objective-C. Objective-c is a superset of C, and the bottom of Objective-C code is implemented by C/C++ code. Objective-c objects and classes are implemented primarily based on C/C++ constructs. Let’s look at the nature of OC objects.

The nature of OC objects

Create an NSObject object and point to it with an obj pointer.

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

We can use the Clang compiler to convert Objective-C code into C/C++ code, which is compiled C/C++ code, not runtime code per se, but can also help us explore the underlying implementation

  • Open the terminal, go to the destination source file directory, and run the following command to obtain the output CPP file.
Xcrun - SDK iphoneos clang -arch arm64-rewrite-objc target OC source file -o Output CPP file main.m as an example -> main-arm64. CPP xcrun - SDK iphoneos  clang -arch arm64 -rewrite-objc main.m -o mani-arm64.cppCopy the code
  • Let’s go in through XcodeNSObjectIn the declaration file, take a lookNSObjectHow is it defined
    • inNSObjectIn the definition of, there are members namelyisa, it isClassThe type of
    • ClassBy keywordtypedefRename the type as can be seenClassThe essence of type isstruct objc_class *Type, which is a pointer to a structure, which meansisaIt’s one that points to a structurePointer to the
    • Inside each object there will be oneisaPointer to the
@interface NSObject { Class isa; }... @end typedef struct objc_class *Class;Copy the code
  • Let’s find it in the generated CPP fileNSObjectThe implementation of: can be seenNSObjectThe compiled class is passedThe structure of the bodyThe implementation. There’s one member of the structure, which is thetaisa
struct NSObject_IMPL {
	Class isa;
};
Copy the code

To sum up, we can conclude that the NSObject class is essentially a struct implementation with an ISA pointer in it, and Pointers are eight bytes in a 64-bit environment.

  • We can also use some of the functions provided by the system to see how much memory is being used.
#import <objc/runtime.h> #import <malloc/malloc.h> int main(int argc, const char * argv[]) { @autoreleasepool { NSObject *obj = [[NSObject alloc] init]; NSLog(@"%zd", class_getInstanceSize([NSObject class])); NSLog(@"%zd", malloc_size((__bridge const void *)obj)); NSLog(@"%zd", malloc_size((__bridge const void *)obj)); // 16 } return 0; }Copy the code

Use the class_getInstanceSize(Class CLS) function to see how much memory is used by a member variable in a Class. Note: This returns the memory size of the aligned member variable

We already know from above that there is only one ISA pointer in the NSObject class, so the print shows that instances in the NSObject class take up 8 bytes.

Extern size_t malloc_size(const void * PTR); This function, which passes a pointer, returns the size of the memory used by the object.

It can be found that the system isNSObjectThe object is allocated 16 bytes of storage, the first 8 bytes of which hold isa Pointers.

Objc4-781 can be explored from the source code:

  • findclass_getInstanceSizeFunction :(take the NSObject class as an example)
    • callalignedInstanceSizefunction
    • callword_alignFunction, passed inunalignedInstanceSize(), instance objectsizeIs 8.
    • word_alignThe result is 8.
// objc-class.mm file size_t class_getInstanceSize(class CLS) {if (! cls) return 0; return cls->alignedInstanceSize(); } // objc-runtime-new.h // Class's ivar size rounded up to a pointer-size boundary. uint32_t alignedInstanceSize() const  { return word_align(unalignedInstanceSize()); } uint32_t unalignedInstanceSize() const { ASSERT(isRealized()); return data()->ro()->instanceSize; } // objc-os.h # define WORD_MASK 7UL static inline size_t word_align(size_t x) { return (x + WORD_MASK) & ~WORD_MASK; }Copy the code
  • exploreallocFunction: callallocFunction forNSObjectObject allocation creates storage space
// NSObject.mm
+ (id)alloc {
    return _objc_rootAlloc(self);
}

// Base class implementation of +alloc. cls is not nil.
// Calls [cls allocWithZone:nil].
id
_objc_rootAlloc(Class cls)
{
    return callAlloc(cls, false/*checkNil*/, true/*allocWithZone*/);
}

// Call [cls alloc] or [cls allocWithZone:nil], with appropriate 
// shortcutting optimizations.
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));
}

// objc-runtime-new.mm
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);
}

// objc-runtime-new.mm
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);
	
  	...
}


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

3. Classification of OC objects

OC objects can be divided into three main categories:

  • instanceObject (Instance object)
  • classObject (Class object)
  • meta-classObject (metaclass object)

Instance object (instance object)

In exploring the nature of NSObject objects, we’re exploring instance objects. Instance objects are created by class instantiation, and each call to alloc creates a new object.

Instance objects store information in memory including isa Pointers and other member variables. Does not include any methods. That is, the method’s memory is not stored in the object’s memory.

Class object (Class object)

  • A class object is the class of an instance object. Why is it also an object, because there is one in a class objectisaPointer to the
NSObject *obj1 = [[NSObject alloc] init]; NSObject *obj2 = [[NSObject alloc] init]; Class objectClass1 = [obj1 Class]; Class objectClass2 = [obj2 class]; // Class objectClass3 = object_getClass(obj1); Class objectClass4 = object_getClass(obj2); Class objectClass5 = [NSObject class]; NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5); // 0x7fff8d86e118 0x7fff8d86e118 0x7fff8d86e118 0x7fff8d86e118 0x7fff8d86e118Copy the code

Class objects can be obtained through the object’s class method, the class method of the class, and the object_getClass function in the Runtime. In the above code, we find that the addresses of the five class objects we get are the same. This means that each class has only one class object in memory.

  • Class objects store information in memory mainly including
    • Isa pointer
    • Superclass pointer
    • Property information of a class, object method information of a class
    • Class protocol information, class member variable information, note not the value of the member variable
    • .

Meta – class object

How do I get a metaclass object? To get a metaclass object, you can only get it using the object_getClass function, passing in the class object as an argument.

Class objectMetaClass = object_getClass([NSObject class]);
Copy the code

How do we prove that we are getting a metaclass object? The runtime provides the corresponding API

NSLog(@"%d", c b
Copy the code

Each class has only one meta-class object in memory.

Meta-class objects have the same memory structure as class objects, but their purpose is different. In other words, they store different things. Meta-class object storage includes the following information:

  • Isa pointer
  • Superclass pointer
  • Class method information for a class
  • .

That is, the meta-class object also contains the class attribute information in its memory structure, but the contents are null

Note: [[NSObject class] class]; The object is not a metaclass object but a class object, which can be determined by class_isMetaClass(), which returns NO.

You can also print [[NSObject class] class]; The returned object address and [NSObject Class]; Return the address, you can see that they are the same.

4. The direction of ISA

The direction of isa is very convoluted in words, and there isa very classic picture as follows:

  • Dotted line represents theisaThe solid line representssuperclassThe point to
  • superclassNeedless to say, this pointer points to its parent class
  • If it’s an instance objectisaRefers to its class object, its class objectisaPoints to its metaclass object

Note: The image above is not an accurate representation at this point. As Apple continues to optimize, Isa no longer refers directly to a class object/metaclass object, but instead finds a class object/metaclass object through the value of &mask, which Apple optimized using the concepts of union and bitfield. But the picture above can help us better understand where ISA is pointing.

The KVO technology takes advantage of the dynamic nature of OC to dynamically add classes and modify the isa pointing at run time by knowing what isa pointing is.