The underlying implementation of objective-C code is actually C\C++ code, so objective-C object-oriented is based on C\C++ data structure

The nature of OC objects

Objective-c objects and classes are mainly implemented based on C\C++ constructs

You can view this by converting the OC code to C++ code with the following command

Clang-rewrite-objc OC source file -o output CPP fileCopy the code

Since Clang converts C++ code differently depending on the platform, use the following command for iOS

/ / to: Iphoneos clang-arch arm64-rewrite-objc OC source file -o CPP file if you need to link to other frameworks Use the -framework argument. For example - framework UIKitCopy the code

Any object that inherits from NSObject comes with an isa member variable of type Class. Turn it into C++ and you can see that NSObject is essentially a structure called NSObject_IMPL. Its member variable isa is also essentially a pointer to the objC_class structure

Memory layout of OC objects

An OC object is laid out in such a way that the system creates a space in the heap to hold the object, which also contains member variables and isa Pointers. And then the local variable in the stack points to the address of the storage space

Memory usage of the OC object

NSObject is automatically allocated 16 bytes of memory, whereas NSObject only occupies 8 bytes of memory. These 8 bytes are the size of the isa pointer to the member variable. The extra 8 bytes are allocated by the system for memory alignment

#import <objc/ Runtime. h> class_getInstanceSize([NSObject class]); #import <malloc/malloc.h> malloc_size((__bridge const void *)obj); NSObject *obj = [[NSObject alloc] init]; NSLog(@"%zd", class_getInstanceSize([NSObject class])); NSLog(@"%zd", malloc_size((__bridge const void *)obj));Copy the code

Validation method

1. Verify the source code

Download apple’s open source framework opensource.apple.com/tarballs/ob…

Select the maximum version download

Find the corresponding code in the objc-Runtime-new.h header file

inline 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 to be at least 16 bytes. // If (size < 16) size = 16; return size; }Copy the code
2. Verify memory

Run Xcode and choose Debug->Debug Workflow -> View Memory to View the Memory data

Enter the memory address of obj and you can see that only the first 8 bytes have values, but 16 bytes of memory space has been allocated

3. Verify LLDB printing

The LLDB memory read is used to read the memory address of the object, which is also allocated 16 bytes

OC Object classification

OC objects fall into three main categories

  • Instance object (instance object)
  • Class object (Class object)
  • Meta-class objects (metaclass objects)

The instance objects

An instance object is an object that comes out of the alloc class, and every time you call that alloc, you create a new instance object

NSObject *object1 = [[NSObject alloc] init]; NSObject *object2 = [[NSObject alloc] init]; NSLog(@"instance - %p %p", object1, object2); NSLog(@"instance - %p %p", object2);Copy the code

The instance object stores information in memory

  • Isa pointer
  • Specific values of other member variables

Class object

Each class has one and only one class object in memory

Class objectClass1 = [object1 class]; Class objectClass2 = [object2 class]; Class objectClass3 = object_getClass(object1); Class objectClass4 = object_getClass(object2); Class objectClass5 = [NSObject class]; // All the above methods return the same class object. NSLog(@"class - %p %p %p %p %p %p %d", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);Copy the code

Note: The class method always returns a class object, so this will still return a class object

Class objectMetaClass2 = [[[NSObject class] class] class];
Copy the code

A class object stores information in memory

  • Isa pointer
  • Superclass pointer
  • Class property information (@property), class object method information (instance method)
  • Class protocol information (Protocol), class member variable information (IVAR)
  • .

Meta – class object

ObjectMetaClass is a meta-class object of NSObject. Each class has one and only one meta-class object in memory

Class objectMetaClass = object_getClass(objectClass5);
Copy the code

Meta-class objects have the same memory structure as class objects, but their purpose is different. The information stored in the memory mainly includes

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

Use class_isMetaClass(Class _Nullable CLS) to see if the Class is a meta-class method

NSLog(@"objectMetaClass - %p %d", objectMetaClass, class_isMetaClass(objectMetaClass));
Copy the code

The isa and the superclass

Each instance object, class object, and metaclass object of a class has an ISA pointer

  • Instance isa points to class
    • When an object method is called, the class is found through instance’s ISA, and finally the implementation of the object method is found to call
  • Class’s ISA points to meta-class
    • When a class method is called, the meta-class is found through the class isa, and the implementation of the class method is finally found to call
  • The ISA of the meta-class points to the meta-class of the base class

Each class has a superclass pointer to its class object, metaclass object

  • The superclass pointer to class points to the class of the superclass
    • If there is no superclass, the superclass pointer is nil
  • The superclass of the meta-class points to the meta-class of the superclass
    • The superclass of the meta-class of the base class points to the class of the base class

Instance calls the path of an object method

  • Superclass = superclass; superclass = superclass

Class calls the trace of a class method

  • Superclass = superclass; superclass = superclass

The underlying structure of the Class type

The objC-runtime-new. h file contains the superclass pointer, the cache method cache, and the class_data_bits_t property table that obtains the specific Class information

struct objc_class : objc_object { // Class ISA; // superclass pointer to Class superclass; // method cache cache_t cache; // formerly cache pointer and vtable // used to get class information class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags The table class_rw_t *data() const {return bits.data(); } void setData(class_rw_t *newData) { bits.setData(newData); }}Copy the code

The inherited parent objc_Object has an ISA pointer

Struct objc_object {private: isa_t isa; public: Class ISA(bool authenticated = false); Class rawISA(); Class getIsa(); uintptr_t isaBits() const; . };Copy the code

Bits & FAST_DATA_MASK can be used to obtain the memory of a class_rw_T table

Struct class_data_bits_t {friend objc_class; class_rw_t* data() const { return (class_rw_t *)(bits & FAST_DATA_MASK); } void setData(class_rw_t *newData) { ASSERT(! data() || (newData->flags & (RW_REALIZING | RW_FUTURE))); uintptr_t newBits = (bits & ~FAST_DATA_MASK) | (uintptr_t)newData; atomic_thread_fence(memory_order_release); bits = newBits; }}Copy the code

Class_rw_t contains a list of methods, attributes, protocols, and class_ro_t attributes

Struct class_rw_t {// Be warned that Symbolication knows the layout of this structure. uint32_t flags; uint16_t witness; #if SUPPORT_INDEXED_ISA uint16_t index; #endif // ro = readonly, only const class_ro_t *ro() const {auto v = get_ro_or_rwe(); if (slowpath(v.is<class_rw_ext_t *>())) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro; } return v.get<const class_ro_t *>(&ro_or_rw_ext); } void set_ro(const class_ro_t *ro) { auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { v.get<class_rw_ext_t *>(&ro_or_rw_ext)->ro = ro; } else { set_ro_or_rwe(ro); } // List of methods const method_array_t methods() const {auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods; } else { return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()}; }} // Property list const property_array_t properties() const {auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties; } else { return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties}; }} // Protocol list const protocol_array_t protocols() const {auto v = get_ro_or_rwe(); if (v.is<class_rw_ext_t *>()) { return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols; } else { return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols}; }}Copy the code

By analyzing the structure of class_ro_t, we can see that instanceSize means the memory space occupied by the instance object, name stores the class name, and ivars stores the list of member variables

struct class_ro_t { uint32_t flags; uint32_t instanceStart; // Uint32_t instanceSize; #ifdef __LP64__ uint32_t reserved; #endif // explicit_atomic<const char *> name; void *baseMethodList; protocol_list_t * baseProtocols; // List of member variables const ivar_list_t * ivars; }Copy the code

Conclusion:

The above analysis can be summarized in a simple graph

Isa pointer

Before the ARM64 architecture, ISA was a plain pointer that stored the memory addresses of Class and meta-class objects

Using the arm64 architecture, isa is optimized to become an isa_t union structure. A union isa block of storage space shared by multiple data structures, including bits, CLS, ISA_BITFIELD structures, and other functions or variables. They all share the same memory space

union isa_t { isa_t() { } isa_t(uintptr_t value) : bits(value) { } uintptr_t bits; private: Class cls; public: #if defined(ISA_BITFIELD) struct { ISA_BITFIELD; // The current version uses a macro to define}; }Copy the code

In isa.h, look at the ISA_BITFIELD structure, where each value isa bit field. The masks and bitfields are different under different architectures, so we only use ARM64 architecture for analysis

/ / in isa. H view ISA_BITFIELD / / behind each variable target number is a domain / / similar ISA_MASK this macro call mask # if __arm64__ # if __has_feature (ptrauth_calls) | | TARGET_OS_SIMULATOR # define ISA_MASK 0x007ffffffffffff8ULL # define ISA_MAGIC_MASK 0x0000000000000001ULL # define ISA_MAGIC_VALUE 0x0000000000000001ULL # define ISA_HAS_CXX_DTOR_BIT 0 # define ISA_BITFIELD \ uintptr_t nonpointer : 1; \ uintptr_t has_assoc : 1; \ uintptr_t weakly_referenced : 1; \ uintptr_t shiftcls_and_sig : 52; \ uintptr_t has_sidetable_rc : 1; \ uintptr_t extra_rc : 8 # define RC_ONE (1ULL<<56) # define RC_HALF (1ULL<<7) # else # define ISA_MASK 0x0000000ffffffff8ULL # define ISA_MAGIC_MASK 0x000003f000000001ULL # define ISA_MAGIC_VALUE 0x000001a000000001ULL # define ISA_HAS_CXX_DTOR_BIT 1 # define ISA_BITFIELD \ uintptr_t nonpointer : 1; \ uintptr_t has_assoc : 1; \ uintptr_t has_cxx_dtor : 1; \ uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \ uintptr_t magic : 6; \ uintptr_t weakly_referenced : 1; \ uintptr_t unused : 1; \ uintptr_t has_sidetable_rc : 1; \ uintptr_t extra_rc : 19 # define RC_ONE (1ULL<<45) # define RC_HALF (1ULL<<18) # endif # elif __x86_64__ .... # else # error unknown architecture for packed isa # endif // SUPPORT_PACKED_ISA #endifCopy the code

The order of the binary bits for each bit field is from right to left, and here is what each bit field means

Similar in the code aboveISA_MASKSuch values are mask to maskISA_MASKFor example, the bits that are converted to binary and found to correspond to 1 are used for values

In addition, there are 33 bits of 1 that correspond to the bits in the bitfield Shiftcls, which in turn stores the addresses of class objects and metaclasses, indicating that more information is stored in isa after arm64. A bit operation of &isa_mask is required to extract the real address values of the class and metaclass objects

The application example of bit operation

Use Commons and bit operations to optimize the memory space of attributes

Create the Person.h file and implement the setter and getter manually

@interface Person : NSObject
//@property (assign, nonatomic, getter=isTall) BOOL tall;
//@property (assign, nonatomic, getter=isRich) BOOL rich;
//@property (assign, nonatomic, getter=isHansome) BOOL handsome;

- (void)setTall:(BOOL)tall;
- (void)setRich:(BOOL)rich;
- (void)setHandsome:(BOOL)handsome;

- (BOOL)isTall;
- (BOOL)isRich;
- (BOOL)isHandsome;

@end
Copy the code

Using the nature of the Commons, create a private Commons type variable in the class extension of Person.m

@interface Person()
{
    union {
        char bits;
        
        struct {
            char tall : 1;
            char rich : 1;
            char handsome : 1;
        };
    } _tallRichHandsome;
}
@end
Copy the code

This pool takes up only 1 byte in total and is allocated according to char bits. In the sturct structure, the 1 of each byte is a bit field, indicating that one bit is occupied. Although it is of type char, the actual occupation is determined according to the value given after the bit field. Tall, rich and handsome all occupy the same memory area, that is, the value is stored in a byte, which is the essence of common body. This is mainly to optimize and save memory space. Char bits determine the size of the allocated space

Since one byte is sufficient for the three variables in the structure, we use one binary bit for each variable. We first set three masks corresponding to three values

// 0x0000 0001
#define TallMask (1<<0)
// 0x0000 0010
#define RichMask (1<<1)
// 0x0000 0100
#define HandsomeMask (1<<2)
Copy the code

The implementation of the setter is as follows: if the parameter is YES, the mask is bitwise or computed; If the parameter is NO, the mask is reversed first, and then bitwise and is performed

@implementation Person

- (void)setTall:(BOOL)tall
{
    if (tall) {
        _tallRichHandsome.bits |= TallMask;
    } else {
        _tallRichHandsome.bits &= ~TallMask;
    }
}

- (void)setRich:(BOOL)rich
{
    if (rich) {
        _tallRichHandsome.bits |= RichMask;
    } else {
        _tallRichHandsome.bits &= ~RichMask;
    }
}

- (void)setHandsome:(BOOL)handsome
{
    if (handsome) {
        _tallRichHandsome.bits |= HandsomeMask;
    } else {
        _tallRichHandsome.bits &= ~HandsomeMask;
    }
}

@end
Copy the code

The implementation of the getter is as follows: perform bitwise and operation on the mask first, and then reverse it twice. Because the return value is a BOOL, it’s either 0 or 1, so anything that’s not 0 has a value, so if you invert it twice you’re either going to get 0 or 1

- (BOOL)isTall { return !! (_tallRichHandsome.bits & TallMask); } - (BOOL)isRich { return !! (_tallRichHandsome.bits & RichMask); } - (BOOL)isHandsome { return !! (_tallRichHandsome.bits & HandsomeMask); }Copy the code

In doing so, we’ve optimized the memory space of the property, and we’ve implemented the setter and getter

The realization of displacement enumeration using bit operation

Create a displacement enumeration with one binary bit for each value

typedef enum {
	OptionsNone = 0,    // 0b0000
  	OptionsOne = 1<<0,   // 0b0001
	OptionsTwo = 1<<1,   // 0b0010
  	OptionsThree = 1<<2, // 0b0100
  	OptionsFour = 1<<3   // 0b1000
} Options;
Copy the code

And the corresponding enumeration value of the bitwise and operation, can find whether the enumeration value exists

@implementation ViewController - (void)setOptions:(Options)options { if (options & OptionsOne) { NSLog(@" includes OptionsOne"); } if (options & OptionsTwo) {NSLog(@" contains OptionsTwo"); } if (options & OptionsThree) {NSLog(@" contains OptionsThree"); } if (options & OptionsFour) {NSLog(@" contains OptionsFour"); } } - (void)viewDidLoad { [super viewDidLoad]; [self setOptions: OptionsOne | OptionsFour]; } @endCopy the code

The interview questions

1. How much memory does an NSObject take up?

The system allocates 16 bytes to the NSObject object (obtained by the malloc_size function)

But NSObject uses only 8 bytes of space internally (available in 64-bit environments via class_getInstanceSize)

2. Look at the following code to describe the memory footprint for Person and Student respectively

@interface Person : NSObject { int _height; } @end @interface Student : Person { int _weight; } @end Person *p = [[Person alloc] init]; NSLog(@"%zd %zd", class_getInstanceSize([Person class]), // 16 malloc_size((__bridge const void *)(p))); // 16 Student *s = [[Student alloc] init]; NSLog(@"%zd %zd", class_getInstanceSize([Student class]), // 16 malloc_size((__bridge const void *)(s))); / / 16Copy the code

By default, on 64-bit processors, since Person inherits from NSObject, the NSObject object is allocated 16 bytes to hold the ISA pointer based on memory alignment. The member of Person, height, is of type Int and takes up four bytes. Because the isa pointer actually occupied only 8 bytes, and redundant space of 8 bytes, so don’t need to allocate more memory, so the Person’s actual and system allocation is 16 bytes (memory alignment with member variables commonly accounted for the biggest multiples to increase: occupies the first 8 bytes isa pointer, occupies the largest, so is the multiple of 8)

Student inherits from Person, isa pointer and member variable height actually take up 12 bytes, plus four extra bytes. The weight member takes up exactly four bytes, so no more memory is allocated, and the actual usage and system allocation of Stuent objects are 16 bytes

3. Look at the following code to describe the memory footprint of Person

@interface Person : NSObject { int _age; int _height; int _no; } @end Person *p = [[Person alloc] init]; NSLog(@"%zd %zd", class_getInstanceSize([Person class]), // 24 malloc_size((__bridge const void *)(p))); / / 32Copy the code

By default, on 64-bit processors, since Person inherits from NSObject, the ISA pointer actually takes up 8 bytes, while Person has three member variables of type Int in it, which actually takes up 12 bytes. The system has to allocate 24 bytes (that is, three times the eight bytes of the ISA pointer) to accommodate all the member variables, so the Person object actually occupies 24 bytes.

But the system allocates memory in multiples of 16, so it has to allocate more than twice the actual number of bytes, so the system allocates 32 bytes for the Person object

Student = + (void)test; Student = + (void)test; Student = + (void)test; Student = + (void)test; Student = + (void)test

@interface NSObject (Test)

+ (void)test;
- (void)test;

@end

@implementation NSObject (Test)

+ (void)test
{
    NSLog(@"+[NSObject test] - %p", self);
}

- (void)test
{
    NSLog(@"-[NSObject test] - %p", self);
}

@interface Person : NSObject

+ (void)test;
- (void)test;
@end

@interface Student : Person

+ (void)test;
- (void)test;
@end


 Student *s = [[Student alloc] init];
[s test];
[Student test];
Copy the code

1.[s test] This method is called when the Student instance looks for the – (void)test method in the Student class according to the isa pointer. If not, the superclass pointer is used to look for the superclass object, and if found, the Person – (void)test method is called. If it doesn’t, it looks for the base NSObject class object based on the superclass pointer. If it does, it calls NSObject’s – (void)test method.

If you comment out NSObject’s – (void)test method, the Student instance will not find the method in the base NSObject class object, because the superclass pointer to NSObject points to nil, and crash

Student’s metaclass looks for the + (void)test method on Student’s metaclass according to the ISA pointer. If Student’s metaclass finds the + (void)test method, it calls Student. If not, the superclass pointer is used to look for the metaclass object of the superclass Person, and if found, the + (void)test method of Person is called. If it doesn’t, it looks in the metaclass object of the base class NSObject based on the superclass pointer. If it does, it calls NSObject’s + (void)test method.

If you comment out NSObject’s + (void)test method, Student’s class object will not find the method in the base NSObject metaclass because the superclass pointer to NSObject’s metaclass points to NSObject’s class object. So it calls the – (void)test method on the NSObject object.

If both methods on NSObject are commented out, then the logic in the previous step would call the – (void)test method on NSObject, and that method would not be found, then the superclass pointer to NSObject would point to nil, and crash would still happen

The messaging mechanism of iOS is essentially message invocation, so it doesn’t really distinguish between categorizing methods and object methods, but looks them up by method name

5. IsMemberOfClass, isKindOfClass, isSubclassOfClass difference, and say the principle

Let’s just print out a little bit of code

Person *person = [[Person alloc] init]; // Person object NSObject *obj = [[NSObject alloc] init]; // NSObject object Class person_class = [person Class]; // Person Class obj_class = [obj Class]; // NSObject Class object Class person_meta_class = object_getClass(person_class); // Person metaclass object Class obj_meta_class = object_getClass(obj_class); // NSObject metaclass object Class person_meta_meta_class = object_getClass(person_meta_class); // NSObject metaclass object Class obj_meta_meta_class = object_getClass(obj_meta_class); // Person object, NSObject object, Person object, NSObject class object NSLog (@ "% @, % @, % @, % @", person, obj, person_class, obj_class); Person metaclass, NSObject metaclass, NSObject metaclass, NSObject NSLog(@"%@, %@, %@, %@, %@", person_meta_class, obj_meta_class, person_meta_meta_class, obj_meta_meta_class);Copy the code

isMemberOfClass

The isMemberOfClass method takes the object to which the ISA pointer points and compares it with the type passed in. Object methods compare the current class object

+ (BOOL)isMemberOfClass:(Class)cls {
    return self->ISA() == cls;
}

- (BOOL)isMemberOfClass:(Class)cls {
    return [self class] == cls;
}
Copy the code

We can compare it with a printed piece of code

// Person class object, Person class object NSLog(@"%d", [Person isMemberOfClass:person_class]); NSObject NSLog(@"%d", [Person isMemberOfClass:obj_class]); // 0 // NSObject NSLog(@"%d", [obj isMemberOfClass:obj_class]); NSLog(@"%d", [person_class isMemberOfClass:person_class]); // 1 // Person metaclclass object, Person class object NSLog(@"%d", [person_class isMemberOfClass:person_class]); // 0 // Person metaclass object, NSObject class object NSLog(@"%d", [person_class isMemberOfClass:obj_class]); // 0 // NSObject NSLog(@"%d", [obj_class isMemberOfClass:obj_class]); // 0 // Person metaclass object, Person metaclass object NSLog(@"%d", [person_class isMemberOfClass:person_meta_class]); // 1 // Person metaclass object, NSObject metaclass object NSLog(@"%d", [person_class isMemberOfClass:obj_meta_class]); // 0 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [obj_class isMemberOfClass:obj_meta_class]); // isa Pointers to all types of metaclass objects point to NSObject metaclass objects, NSObject NSLog(@"%d", [person_meta_class isMemberOfClass:person_meta_class]); // 0 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [person_meta_class isMemberOfClass:obj_meta_class]); // 1 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [obj_meta_class isMemberOfClass:obj_meta_class]); / / 1Copy the code

isKindOfClass

The isKindOfClass class method takes the object to which the ISA pointer points and the object to which the superclass pointer points and compares it with the type passed in; Object methods compare the current class object with the object to which the superclass pointer to that object points

+ (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = self->ISA(); tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}

- (BOOL)isKindOfClass:(Class)cls {
    for (Class tcls = [self class]; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}
Copy the code

We can compare it with a printed piece of code

NSLog(@"%d", [Person isKindOfClass:person_class]); NSObject NSLog(@"%d", [Person isKindOfClass:obj_class]); NSLog(@"%d", [obj isKindOfClass:obj_class]); NSLog(@"%d", [obj isKindOfClass:obj_class]); NSLog(@"%d", [person_class isKindOfClass:person_class]); // the superclass of the Person metaclass refers to the NSObject metaclass, And the superclass of the NSObject metaclass refers to the NSObject metaclass, the Person metaclass, the NSObject metaclass NSLog(@"%d", [person_class isKindOfClass:obj_class]); // 1 // NSObject NSLog(@"%d", [obj_class isKindOfClass:obj_class]); NSLog(@"%d", [person_class isKindOfClass:person_meta_class]); // 1 // Person metaclass object, NSObject metaclass object NSLog(@"%d", [person_class isKindOfClass:obj_meta_class]); // 1 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [obj_class isKindOfClass:obj_meta_class]); // 1 // NSObject metaclass object, Person metaclass object NSLog(@"%d", [person_meta_class isKindOfClass:person_meta_class]) // 0 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [person_meta_class isKindOfClass:obj_meta_class]); // 1 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [obj_meta_class isKindOfClass:obj_meta_class]); / / 1Copy the code

isSubclassOfClass

The isSubclassOfClass class method compares the current class object and the object pointed to by the SuperClass pointer with the type passed in; This method has no object methods

+ (BOOL)isSubclassOfClass:(Class)cls {
    for (Class tcls = self; tcls; tcls = tcls->getSuperclass()) {
        if (tcls == cls) return YES;
    }
    return NO;
}
Copy the code

We can compare it with a printed piece of code

// Person class object, Person class object NSLog(@"%d", [person_class isSubclassOfClass:person_class]); // 1 // Person object, NSObject object NSLog(@"%d", [person_class isSubclassOfClass:obj_class]); // 1 // NSObject NSLog(@"%d", [obj_class isSubclassOfClass:obj_class]); NSLog(@"%d", [person_class isSubclassOfClass:person_meta_class]); NSLog(@"%d", [person_class isSubclassOfClass:person_meta_class]); // 0 // Person class object, NSObject metaclass object NSLog(@"%d", [person_class isSubclassOfClass:obj_meta_class]); // 0 // NSObject NSLog(@"%d", [obj_class isSubclassOfClass:obj_meta_class]); // 0 // Person metaclass object, Person metaclass object NSLog(@"%d", [person_meta_class isSubclassOfClass:person_meta_class]); // 1 // Person metaclass object, NSObject metaclass object NSLog(@"%d", [person_meta_class isSubclassOfClass:obj_meta_class]); // 1 // NSObject metaclass object, NSObject metaclass object NSLog(@"%d", [obj_meta_class isSubclassOfClass:obj_meta_class]); NSLog(@"%d", [person_meta_class isSubclassOfClass:person_class]); NSLog(@"%d", [person_meta_class isSubclassOfClass:person_class]); // 0 // Person metaclass object, NSObject class object NSLog(@"%d", [person_meta_class isSubclassOfClass:obj_class]); // 1 // NSObject NSLog(@"%d", [obj_meta_class isSubclassOfClass:obj_class]); / / 1Copy the code