Objective-c objects, OC objects for short, fall into three main categories

1. Instance object

  • instanceAn object is an object that comes out of the alloc class, and every time you call alloc, a new instance object is created
  • The code performance
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Copy the code
  • Object1, object2 are NSObjectinstanceObject (The instanceObject)
  • They are two different objects occupying two different pieces of memory
  • instaceObject stores information in memory including
    • isaPointer to the
    • otherMember variables

2. Class object

  • The code performance
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1); // Runtime
Class objectClass5 = object_getClass(object2); // Runtime
Copy the code
  • ObjectClass1 ~ objectClass5 are all NSObjectclassObject (class object), the class method always returnsclassobject
  • They’re all the same object. Each class has one and only one in memoryclassobject
  • classObject stores information in memory mainly including
    • isaPointer to the
    • superclassPointer to the
    • Of the classattributeInformation (@property), the classObject methodsInformation (instance method)
    • Of the classagreementInformation (protocol), the classMember variablesInformation (ivar)

3. Meta-class objects (metaclass objects)

  • The code performance
Class objectMetaClass = object_getClass([NSObject class]); // Runtime
Copy the code
  • ObjectMetaClass isNSObjectthemeta-classobject
  • Each class has one and only one in memorymeta-classobject
  • meta-classThe objects andclassThe memory structure of objects is the same, but the purpose is different. The information stored in the memory mainly includes
    • isaPointer to the
    • superclassPointer to the
    • Class method information for class (class method)
  • To viewClassWhether it ismeta-class
BOOL result = class_isMetaClass([NSObject class]); // Runtime
Copy the code

Object_getClass internal implementation

  • Some of the underlying implementations have changed due to objective-C and Swift mixing issues
Class objc_getClass(const char *aClassName)
{
    if(! aClassName)return Nil;

    // NO unconnected, YES class handler
    return look_up_class(aClassName, NO.YES);
}

Class look_up_class(const char *name, 
              bool includeUnconnected __attribute__((unused)), 
              bool includeClassHandler __attribute__((unused)))
{
    // Return nil if the class name is empty
    if(! name)return nil;

    // Define the Class result result
    Class result;
    // The variable unrealized is not realized
    bool unrealized;
    {
        / / lock
        runtimeLock.lock();
        // Get the non-swift Class
        result = getClassExceptSomeSwift(name);
        // If result has a value and the result class method is implementedunrealized = result && ! result->isRealized();if (unrealized) {
            / / unrealized, would call realizeClassMaybeSwiftAndUnlock implementation, at the same time to unlock
            result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock);
            // runtimeLock is now unlocked
        } else {
            / / unlockruntimeLock.unlock(); }}if(! result) {// Ask Swift about its un-instantiated classes.

        // We use thread-local storage to prevent infinite recursion
        // if the hook function provokes another lookup of the same name
        // (for example, if the hook calls objc_allocateClassPair)

        auto *tls = _objc_fetch_pthread_data(true);

        // Stop if this thread is already looking up this name.
        for (unsigned i = 0; i < tls->classNameLookupsUsed; i++) {
            if (0 == strcmp(name, tls->classNameLookups[i])) {
                return nil; }}// Save this lookup in tls.
        if (tls->classNameLookupsUsed == tls->classNameLookupsAllocated) {
            tls->classNameLookupsAllocated =
                (tls->classNameLookupsAllocated * 2? :1);
            size_t size = tls->classNameLookupsAllocated *
                sizeof(tls->classNameLookups[0]);
            tls->classNameLookups = (const char **)
                realloc(tls->classNameLookups, size);
        }
        tls->classNameLookups[tls->classNameLookupsUsed++] = name;

        // Call the hook.
        Class swiftcls = nil;
        if (GetClassHook.get()(name, &swiftcls)) {
            ASSERT(swiftcls->isRealized());
            result = swiftcls;
        }

        // Erase the name from tls.
        unsigned slot = --tls->classNameLookupsUsed;
        ASSERT(slot >= 0  &&  slot < tls->classNameLookupsAllocated);
        ASSERT(name == tls->classNameLookups[slot]);
        tls->classNameLookups[slot] = nil;
    }

    return result;
}
Copy the code

Tell me about the isa

  • instanceThe object’sisaPoint to theclass
    • When callingObject methodsWhen, throughinstancetheisafindclassAnd finally foundObject methodsThe implementation of the
  • classtheisaPoint to themeta-class
    • When callingClass methodWhen, throughclasstheisafindmeta-classAnd finally foundClass methodThe implementation of the
  • meta-classtheisaPoint to theThe base classthemeta-class

Tell me something about the superclass

  • classthesuperclassPoint to themeta-class
    • If there is no parent class,superclassA pointer tonil
  • meta-classthesuperclassPoint to theThe base classthemeta-class
    • The base classmeta-classthesuperclassPoints to the base classclass
  • In example 1(the instance object calls the parent’s object method),Student’s instance calls Person’s object method flow

  • In example 2(the class object calls the class method of the parent class),Student’s class object calls the class method flow of the Person class

Classic ISA and Superclass diagrams

  • Inttance Indicates the path of calling an object method
    • When isa finds a method that does not exist, it uses superclass to find a method that does not exist. When isa finds a method that does not exist, it uses superclass to find a method that does not exist. When isa finds a method that does not exist, it uses superclass to find a method that does not existunrecognized selector sent to instance 0xxxxxxxxx
  • Class calls the trace of a class method
    • Superclass = superclass; superclass = superclass
    • This has a special case 🙊, find meta-class is not found, becausemeta-classthesuperclassPoint to theThe base classthemeta-classIs calledThe base classthemeta-classThis is not surprising when you call a class method and end up calling an object method of the base class.
      • Sample code is as follows
      #import <Foundation/Foundation.h>
      #import <objc/objc.h>
      
      @interface XYPerson : NSObject
      
      + (void)test;
      
      @end
      
      @implementation XYPerson
      
      @end
      
      @interface NSObject (Test)
      
      + (void)test;
      
      @end
      
      @implementation NSObject (Test)
      
      - (void)test
      {
          NSLog(@"-[NSObject test] - %p".self);
      }
      
      @end
      
      int main(int argc, const char * argv[]) {
          @autoreleasepool {
              // [XYPerson class] - 0x1000041e0
              NSLog(@"[XYPerson class] - %p", [XYPerson class]);
              // [NSObject class] - 0x7fff90dd4118
              NSLog(@"[NSObject class] - %p"[NSObject class]);
      
              // -[NSObject test] - 0x1000041e0
              [XYPerson test];
      // objc_msgSend([XYPerson class], @selector(test));
      
              // -[NSObject test] - 0x7fff90dd4118
              [NSObject test];
      // objc_msgSend([NSObject class], @selector(test));
          }
          return 0;
      }
      Copy the code

Talk about ISA and Superclass in detail

  • Code signal
// Address of the MJPerson class object: 0x00000001000014c8
// Isa of the MJPerson instance object: 0x001D8001000014c9
// isa & ISA_MASK: 0x00000001000014c8

MJPerson *person = [[MJPerson alloc] init];

Class personClass = [MJPerson class];

Class personMetaClass = object_getClass(personClass);
Copy the code
  • Starting from 64bit,isa requires a bit operation to calculate the actual ISA address value
    • isa & ISA_MASK
    • The value of the ISA_MASK
  • superclassYou do not need to perform this operation, but directly use the corresponding address value
  • objc_msgSendIs to useassemblyBecause the call is very frequent, useassemblyCan improve the efficiency of execution

Isa and superclass-class and meta-class structures

  • The code structure
/// Represents an instance of a class.
struct objc_object {
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};

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 {
        returnbits.data(); }... }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

    explicit_atomic<uintptr_t> ro_or_rw_ext;

    Class firstSubclass;
    Class nextSiblingClass;
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;
#ifdef __LP64__
    uint32_t reserved;
#endif

    union {
        const uint8_t * ivarLayout;
        Class nonMetaclass;
    };

    explicit_atomic<const char *> name;
    // With ptrauth, this is signed if it points to a small list, but
    // may be unsigned if it points to a big list.
    void *baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;

    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};
Copy the code
  • Image structure

Simulate the structure inside a class

#import <Foundation/Foundation.h>

#ifndef MJClassInfo_h
#define MJClassInfo_h

# if __arm64__
#   define ISA_MASK        0x0000000ffffffff8ULL
# elif __x86_64__
#   define ISA_MASK        0x00007ffffffffff8ULL
# endif

#if __LP64__
typedef uint32_t mask_t;
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;

struct bucket_t {
    cache_key_t _key;
    IMP _imp;
};

struct cache_t {
    bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;
};

struct entsize_list_tt {
    uint32_t entsizeAndFlags;
    uint32_t count;
};

struct method_t {
    SEL name;
    const char *types;
    IMP imp;
};

struct method_list_t : entsize_list_tt {
    method_t first;
};

struct ivar_t {
    int32_t *offset;
    const char *name;
    const char *type;
    uint32_t alignment_raw;
    uint32_t size;
};

struct ivar_list_t : entsize_list_tt {
    ivar_t first;
};

struct property_t {
    const char *name;
    const char *attributes;
};

struct property_list_t : entsize_list_tt {
    property_t first;
};

struct chained_property_list {
    chained_property_list *next;
    uint32_t count;
    property_t list[0];
};

typedef uintptr_t protocol_ref_t;
struct protocol_list_t {
    uintptr_t count;
    protocol_ref_t list[0];
};

struct class_ro_t {
    uint32_t flags;
    uint32_t instanceStart;
    uint32_t instanceSize;  // The memory space occupied by the instance object
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;  / / the name of the class
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // List of member variables
    const uint8_t * weakIvarLayout;
    property_list_t *baseProperties;
};

struct class_rw_t {
    uint32_t flags;
    uint32_t version;
    const class_ro_t *ro;
    method_list_t * methods;    // List of methods
    property_list_t *properties;    // Attribute list
    const protocol_list_t * protocols;  // Protocol list
    Class firstSubclass;
    Class nextSiblingClass;
    char *demangledName;
};

#define FAST_DATA_MASK          0x00007ffffffffff8UL
struct class_data_bits_t {
    uintptr_t bits;
public:
    class_rw_t* data() {
        return(class_rw_t *)(bits & FAST_DATA_MASK); }};/* OC object */
struct mj_objc_object {
    void *isa;
};

/* Class object */
struct mj_objc_class : mj_objc_object {
    Class superclass;
    cache_t cache;
    class_data_bits_t bits;
public:
    class_rw_t* data() {
        return bits.data();
    }
    
    mj_objc_class* metaClass() {
        return (mj_objc_class *)((long long)isa & ISA_MASK); }};#endif /* MJClassInfo_h */

// objective-c++
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import "MJClassInfo.h"

// MJPerson
@interface MJPerson : NSObject <NSCopying>
{
@public
    int _age;
}
@property (nonatomic.assign) int no;
- (void)personInstanceMethod;
+ (void)personClassMethod;
@end

@implementation MJPerson

- (void)test
{
    
}

- (void)personInstanceMethod
{
    
}
+ (void)personClassMethod
{
    
}
- (id)copyWithZone:(NSZone *)zone
{
    return nil;
}
@end

// MJStudent
@interface MJStudent : MJPerson <NSCoding>
{
@public
    int _weight;
}
@property (nonatomic.assign) int height;
- (void)studentInstanceMethod;
+ (void)studentClassMethod;
@end

@implementation MJStudent
- (void)test
{
    
}
- (void)studentInstanceMethod
{
    
}
+ (void)studentClassMethod
{
    
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
    return nil;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    
}
@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        MJStudent *stu = [[MJStudent alloc] init];
        stu->_weight = 10;
        
        mj_objc_class *studentClass = (__bridge mj_objc_class *)([MJStudent class]);
        mj_objc_class *personClass = (__bridge mj_objc_class *)([MJPerson class]);
        
        class_rw_t *studentClassData = studentClass->data();
        class_rw_t *personClassData = personClass->data();
        
        class_rw_t *studentMetaClassData = studentClass->metaClass()->data();
        class_rw_t *personMetaClassData = personClass->metaClass()->data();

        NSLog(@ "1111");
    }
    return 0;
}
Copy the code

The interview questions

  1. Where does the isa pointer to the object point?
    • instanceThe object’sisaPoint to theclassobject
    • classThe object’sisaPoint to themeta-classobject
    • meta-classThe object’sisaPoint to theThe base classthemeta-classobject
  2. Where is the OC class information stored?
    • Object methods, attributes, member variables, protocol information, stored inclassIn the object
    • Class methodAnd stored inmeta-classIn the object
    • Member variablesThe specific value is stored ininstanceobject