IOS instance objects, classes, metaclasses iOS instance objects, classes, metaclass

The author recently read some contents about instance object, class and metaclass. Will be shared in this article.

First let’s look at the definitions of instance objects, classes, and metaclasses.

Class, metaclass definition

Metaclass: In object-oriented programming, a metaclass (English: metaclass) is aclass whose instance is aclass. While a normal class defines the behavior of a particular object, a metaclass defines the behavior of a particular class and its objects. From the 360 encyclopedia metaclass

Class: In object-oriented programming, a class (English: class) is a construct of the object-oriented computer programming language. It is a blueprint for creating objects, describing the common properties and methods of the objects created. Quote from wikipedia class

The metaclass is the description of the class object, just like the class is the description of ordinary instances. From [objc explain]: Classes and metaclasses

In a nutshell:

A class is a description of an instance object, and a metaclass is a description of a class object. For example, NSObject *obj = [NSObject new]; NSObject is the description of the instance object OBj, and the metaclass of NSObject is the description of the class object of NSObject.

Relationships between instance objects, classes, and metaclasses

Let’s look at the relationship between instance objects, classes, and metaclasses.

[objc explain] Classes and metaclasses

According to the figure above, we can sort out the relationship among instance objects, classes, and metaclasses.

Isa pointing and parent pointing of instance object, class object, metaclass

Below I illustrate the isa and superclass reference in the figure above.

  1. Isa Pointers to instance objects point to class objects;

  2. Isa Pointers to class objects point to metaclass objects;

  3. Isa Pointers to metaclass objects point to the root metaclass (NSObject metaclass);

  4. The isa pointer to the root metaclass (NSObject metaclass) points to itself;

  5. The parent of a subclass’s metaclass points to the parent class’s metaclass;

  6. NSObject’s parent class is nil;

  7. The parent of the root metaclass (NSObject metaclass) points to NSObject.

0. Description of the functions used below
#import 
      

If obj is an instance object, obj is a metaclass object, obj is a metaclass object, obj is a metaclass object.
OBJC_EXPORT Class _Nullable object_getClass(id _Nullable obj) ;

// Returns the metaclass with the specified class name name
OBJC_EXPORT Class _Nullable objc_getMetaClass(const char * _Nonnull name);

// Check whether the specified class is a metaclass
OBJC_EXPORT BOOL class_isMetaClass(Class _Nullable cls);

// Returns the parent of the specified class
OBJC_EXPORT Class _Nullable class_getSuperclass(Class _Nullable cls) ;
Copy the code

We can use code to verify the correctness of the instance object, class, and metaclass diagrams. In order to verify isa pointing and superpointing of object, class object, metaclass, we created two classes, namely QiSuperClass and QiSubClass. QiSuperClass inherits from NSObject and QiSubClass inherits from QiSuperClass.

The author wrote the following sample code to verify.

/ /! Validates isa and superclass references for instance objects, class objects, and metaclass objects
- (void)instanceClassMetaClassISASuperClicked {
    
    QiSubClass *subClassInstance = [QiSubClass new];
    / / class object
    Class subClass = object_getClass(subClassInstance);
    // Metaclass object
    Class subMetaClass = object_getClass(subClass);
    // The metaclass object of the metaclass object
    Class subMetaClassMetaClass = object_getClass(subMetaClass);
    
    // 1. The isa pointer to the instance object points to the class object
    QiLog(@"----1. Isa pointer to instance object ----");
    QiLog(@"QiSubClass address: %p, QiSubClass address: %p", subClass, [QiSubClass class]);
    
    // 2. Isa pointer to a metaclass;
    QiLog(@"----2. Isa pointer to a metaclass; -");
    QiLog(QiSubClass metaclass address: %p, QiSubClass metaclass address: %p", subMetaClass, objc_getMetaClass("QiSubClass"));
    
    // 3. Isa pointer to the root metaclass (NSObject metaclass);
    QiLog(----3. Isa pointer to the root metaclass (NSObject metaclass) ----");
    QiLog("QiSubClass metaclass address: %p, root metaclass (NSObject metaclass) object address: %p", subMetaClassMetaClass, objc_getMetaClass("NSObject"));
    
    // 4. Isa pointer to the root metaclass (NSObject metaclass) points to itself;
    QiLog(@"----4. Isa pointer to the root metaclass (NSObject metaclass) points to itself ----");
    QiLog(@" Root metaclass object address: %p, root metaclass object ISA pointer address: %p", objc_getMetaClass("NSObject"), object_getClass(objc_getMetaClass("NSObject")));
    
    // 1. The parent of a subclass's metaclass refers to the parent metaclass;
    QiLog(1. The parent of a subclass's metaclass points to the parent metaclass ----");
    QiLog(@"QiSubClass metaclass: %p, QiSuperClass metaclass: %p", class_getSuperclass(objc_getMetaClass("QiSubClass")), objc_getMetaClass("QiSuperClass"));
    
    // 2. NSObject's parent class is nil;
    QiLog(@"----2. NSObject parent is nil----");
    QiLog(@" Parent object of NSObject: %@", class_getSuperclass([NSObject class]));
    
    // 3. The parent of the root metaclass (NSObject metaclass) points to NSObject.
    QiLog(@"----3. Parent of the root metaclass (NSObject metaclass) points to NSObject----");
    QiLog(@" Root metaclass (NSObject metaclass) parent address: %p, NSObject class address: %p", class_getSuperclass(objc_getMetaClass("NSObject")),NSObject class]);
}

Copy the code

The output results are as follows:

---1.The isa pointer to the instance object points to the address of the class object ---- QiSubClass:0x10dad86c0QiSubClass object address:0x10dad86c0

---- 2.Isa Pointers to class objects point to metaclass objects; ---- QiSubClass metaclass address:0x10dad8698QiSubClass metaclass object address:0x10dad8698

---- 3.Isa Pointers to metaclass objects point to the root metaclass (NSObjectMetaclass) ---- QiSubClass Metaclass address of a metaclass object:0x7fff86cb8638, the root metaclass (NSObjectObject address of metaclass) :0x7fff86cb8638
    
---- 4.Root metaclass (NSObjectIsa pointer to ---- root metaclass (NSObjectObject address of metaclass) :0x7fff86cb8638, the root metaclass (NSObjectIsa pointer address to the object of metaclass:0x7fff86cb8638
    
---1.The parent of a subclass's metaclass refers to the parent metaclass ---- The parent object address of QiSubClass's metaclass:0x10dad8508The metaclass address of QiSuperClass:0x10dad8508
    
---- 2. NSObjectThe parent classnil----
NSObjectParent object of :(null) --- 3.Root metaclass (NSObjectMetaclass)NSObject---- Root metaclass (NSObjectSuperclass address of metaclass) :0x7fff86cb8660.NSObjectAddress:0x7fff86cb8660
Copy the code

The code above may be a bit confusing when viewed together, but I use LLDB to output the address of the object. To view the relationships between instance objects, class objects, and metaclass objects respectively.

1. The ISA pointer of the instance object points to the class object;

The class object address of QiSubClass is 0x0000000104173580.

(lldb) p/x [QiSubClass class]
(Class) $1 = 0x0000000104173580 QiSubClass

(lldb) p/x object_getClass([QiSubClass new])
(Class _Nullable) $2 = 0x0000000104173580 QiSubClass
Copy the code
2. Isa pointer to a metaclass;

QiSubClass’s metaclass object address is 0x0000000104173558.

(lldb) p/x object_getClass([QiSubClass class])
(Class _Nullable) $3 = 0x0000000104173558
(lldb) p/x objc_getMetaClass("QiSubClass")
(Class _Nullable) $4 = 0x0000000104173558
Copy the code
3. Isa Pointers to metaclass objects point to the root metaclass (NSObject metaclass);

The address of the metaclass object for QiSubClass and NSObject are both 0x00007FFF86CB8638.

(lldb) p/x object_getClass(objc_getMetaClass("QiSubClass"))
(Class _Nullable) $5 = 0x00007fff86cb8638
(lldb) p/x objc_getMetaClass("NSObject")
(Class _Nullable) $6 = 0x00007fff86cb8638
Copy the code
4. The isa pointer to the root metaclass (NSObject metaclass) points to itself;

Look at the address of NSObject’s metaclass object and the address of NSObject’s metaclass object is 0x00007FFF86CB8638.

(lldb) p/x objc_getMetaClass("NSObject")
(Class _Nullable) $6 = 0x00007fff86cb8638
(lldb) p/x object_getClass(objc_getMetaClass("NSObject"))
(Class _Nullable) $7 = 0x00007fff86cb8638
Copy the code
1. The parent of a subclass’s metaclass points to the parent metaclass;

The address of the superclass of QiSubClass and the address of the metaclass of QiSuperClass are 0x00000001041733c8.

(lldb) p/x class_getSuperclass(objc_getMetaClass("QiSubClass"))
(Class _Nullable) $11 = 0x00000001041733c8
(lldb) p/x objc_getMetaClass("QiSuperClass")
(Class _Nullable) $12 = 0x00000001041733c8
Copy the code
2. NSObject’s parent class is nil;

See NSObject’s parent object is nil.

(lldb) po class_getSuperclass([NSObject class])
nil
Copy the code
3. The parent of the root metaclass (NSObject metaclass) points to NSObject.

Check that the address of the parent of the NSObject metaclass and the address of the NSObject metaclass are both 0x00007FFF86CB8660.

(lldb) p/x class_getSuperclass(objc_getMetaClass("NSObject"))
(Class _Nullable) $16 = 0x00007fff86cb8660 NSObject
(lldb) p/x [NSObject class]
(Class) $17 = 0x00007fff86cb8660 NSObject
Copy the code

Change the ISA reference of class objects and metaclass objects

QiSuperClass; QiSubClass; ISA;

@interface QiSuperClass : NSObject
    
- (void)superMethod;
+ (void)superClassMethod;

@end

NS_ASSUME_NONNULL_END
Copy the code
#import "QiSuperClass.h"

@implementation QiSuperClass

- (void)superMethod {
    
    NSLog(@"superMethod");
}

+ (void)superClassMethod {
    
    NSLog(@"SuperClassMethod");
}

@end

Copy the code
NS_ASSUME_NONNULL_BEGIN

@interface QiSubClass : QiSuperClass

- (void)subMethod;
+ (void)subClassMethod;

@end

NS_ASSUME_NONNULL_END
Copy the code
#import "QiSubClass.h"

@implementation QiSubClass

- (void)subMethod {
    
    NSLog(@"subMethod");
}

+ (void)subClassMethod {
    
    NSLog(@"subClassMethod");
}

@end
Copy the code

Guess the result of the following code

    QiSuperClass *superCls = [QiSuperClass new];
    [superCls superMethod];
    QiLog(SuperCls object current type: %@, superCls.class);

    [((QiSubClass *)superCls) subMethod];
    [QiSuperClass performSelector:@selector(subClassMethod)];
Copy the code

SuperCls is an instance of QiSuperClass. SuperCls does not have an instance method of QiSubClass. Similarly, the superclass QiSuperClass has no class methods for the subclass QiSubClass. Furthermore, if you look at the list of methods of a class, or the list of methods of a metaclass, you’ll see why (I’ll explain why in a future article).

Guess the result of the following code

    QiSuperClass *superCls = [QiSuperClass new];
    [superCls superMethod];
    
    QiLog(SuperCls object current type: %@, superCls.class);
    // Sets the class of an object. Sets the class of an object
    object_setClass(superCls, QiSubClass.class);
    QiLog(SuperCls object current type: %@, superCls.class);
    [((QiSubClass *)superCls) subMethod];
    
    QiLog(@"QiSuperClass metaclass object type: %@", objc_getMetaClass("QiSuperClass"));
    object_setClass(QiSuperClass.class, objc_getMetaClass("QiSubClass"));
    QiLog(@"QiSuperClass metaclass object type: %@", objc_getMetaClass("QiSuperClass"));
    
    [QiSuperClass performSelector:@selector(subClassMethod)];
Copy the code

The output is as follows:

SuperMethod superCls object type: QiSuperClass superCls object type: QiSubClass subMethod Metaclass address of QiSuperClass:0x10d6e3a10QiSuperClass metaclass address:0x10d6e3ba0QiSubClass metaclass address:0x10d6e3ba0
subClassMethod
Copy the code

Yesterday a colleague [dacheng small stack] (www.jianshu.com/u/455ee5a38)… Ask me about my object_setClass application scenario. I have not used object_setClass in my own projects. If you have an object_setClass scenario in your project, please feel free to comment. Object_setClass is used in KVO implementation. If you have seen the partial implementation of KVO, you may know that when we assign a listener class, the system dynamically creates a subclass of the listener class and changes the ISA pointer of the listener object to the newly created subclass. The author uses the following code to make a preliminary suggestion. More on that in a future article.

@interface QiRuntimeTableViewController ()

@property (nonatomic, copy) NSString *observeString;

@end
Copy the code

Call the following code

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@ "self class: % @"[self class]);
    NSLog(@ "object_getClass: % @", object_getClass(self));
    [self addObserver:self forKeyPath:@"observeString" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@ "self class: % @"[self class]);
    NSLog(@ "object_getClass: % @", object_getClass(self));
}
Copy the code

The code above results in:

self class: QiRuntimeTableViewController object_getClass: QiRuntimeTableViewControllerself class: QiRuntimeTableViewController object_getClass:NSKVONotifying_QiRuntimeTableViewController
Copy the code

If you look at the result, you’ll see that the ISA pointer to the instance object self has changed. Pointed to the stylish create class NSKVONotifying_QiRuntimeTableViewController operation, The author thinks that this step is called object_setClass (self, NSClassFromString (@ “NSKVONotifying_QiRuntimeTableViewController”));

I’m not going to put the object_setClass source code here. I’ve posted the object_getClass source code below. If you’re interested, go ahead and check it out or download the Runtime source code for details. The runtime source download address: opensource.apple.com/tarballs/ob…

Object_getClass object_getClass

1. Object_getClass source code
Class object_getClass(id obj)
{
    if (obj) return obj->getIsa();
    else return Nil;
}
Copy the code

The source code literal of object_getClass indicates that this function is used to get the ISA pointer to the specified object. The specific call details are as follows.

2. Objc_class structure source code

The following code shows that the Class type is equivalent to objc_class *.

/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
Copy the code

The objc_class structure is as follows:

struct objc_class : objc_object {
    // Class ISA;
    Class superclass;
    cache_t cache;             // formerly cache pointer and vtable
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags

    class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }

    void setInfo(uint32_t set) {
        ASSERT(isFuture()  ||  isRealized());
        data()->setFlags(set);
    }

    void clearInfo(uint32_t clear) {
        ASSERT(isFuture()  ||  isRealized());
        data()->clearFlags(clear);
    }
    // omit some code.....
}
Copy the code
3. objc_object structure source code
struct objc_object {
private:
    isa_t isa;

public:

    // ISA() assumes this is NOT a tagged pointer object
    Class ISA();

    // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
    Class rawISA();

    // getIsa() allows this to be a tagged pointer object
    Class getIsa();
    
    uintptr_t isaBits() const;
    // More code...
}
Copy the code
4. GetIsa () the source code
#if SUPPORT_TAGGED_POINTERS

inline Class 
objc_object::getIsa() 
{
    if(fastpath(! isTaggedPointer()))return ISA();

    extern objc_class OBJC_CLASS_$___NSUnrecognizedTaggedPointer;
    uintptr_t slot, ptr = (uintptr_t)this;
    Class cls;

    slot = (ptr >> _OBJC_TAG_SLOT_SHIFT) & _OBJC_TAG_SLOT_MASK;
    cls = objc_tag_classes[slot];
    if (slowpath(cls == (Class)&OBJC_CLASS_$___NSUnrecognizedTaggedPointer)) {
        slot = (ptr >> _OBJC_TAG_EXT_SLOT_SHIFT) & _OBJC_TAG_EXT_SLOT_MASK;
        cls = objc_tag_ext_classes[slot];
    }
    return cls;
}
Copy the code
5. The ISA () the source code
#if SUPPORT_NONPOINTER_ISA

inlineClass objc_object::ISA() { ASSERT(! isTaggedPointer());#if SUPPORT_INDEXED_ISA
    if (isa.nonpointer) {
        uintptr_t slot = isa.indexcls;
        return classForIndex((unsigned)slot);
    }
    return (Class)isa.bits;
#else
    return (Class)(isa.bits & ISA_MASK);
#endif
}
Copy the code

Refer to study website

[objc explain]: Classes and metaclasses What is a meta-class in Objective-C? Friday Q&A 2010-11-6: Creating Classes at Runtime in Objective-C Objective-c Runtime: Isa and Class

To learn more about iOS and related new technologies, please follow our official account:

QiShare(Simple book) QiShare(digging gold) QiShare(Zhihu) QiShare(GitHub) QiShare(CocoaChina) QiShare(StackOverflow) QiShare(wechat public account)

Recommend the article

StatelessWidget and its lifecycle in Flutter RenderObjectElement and RenderObjectWidget IOS fix [NSURL fileURLWithPath:] # encoding problem Xcode adjust navigation directory font size B Swift 5.1 (21) – Generic Swift 5.1 (20) – protocol Swift 5.1 (19) – Extended Swift 5.1 (18) – Nested Types A brief introduction to the compilation process