美文网首页iOS沉淀IOS开发知识点
iOS面试题-OC对象的isa指针

iOS面试题-OC对象的isa指针

作者: 小荣袁 | 来源:发表于2021-03-21 20:06 被阅读0次

Objective-C中的对象,简称OC对象,主要可以分为3种

1. instance对象(实例对象)

  • instance对象就是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象
  • 代码表现
NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
  • object1, object2都是NSObject的instance对象(实例对象)
  • 它们是不同的两个对象,分别占据着两块不同的内存
  • instace对象在内存中存储的信息包括
    • isa指针
    • 其它成员变量

2. class对象(类对象)

  • 代码表现
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
  • objectClass1 ~ objectClass5都是NSObject的class对象(类对象),class方法返回的一直是class对象
  • 它们都是同一个对象.每个类在内存中有且只有一个class对象
  • class对象在内存中存储的信息主要包括
    • isa指针
    • superclass指针
    • 类的属性信息(@property),类的对象方法信息(instance method)
    • 类的协议信息(protocol),类的成员变量信息(ivar)

3. meta-class对象(元类对象)

  • 代码表现
Class objectMetaClass = object_getClass([NSObject class]); // Runtime
  • objectMetaClass是NSObjectmeta-class对象
  • 每个类在内存中有且只有一个meta-class对象
  • meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括
    • isa指针
    • superclass指针
    • 类的类方法信息(class method)
  • 查看Class是否为meta-class
BOOL result = class_isMetaClass([NSObject class]); // Runtime

object_getClass的内部实现

image.png
  • 因为Objective-C和swift的混编问题,一些底层实现有所变化
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)))
{
    // 传过来的类名为空的话,直接返回nil
    if (!name) return nil;

    // 定义Class结果result
    Class result;
    // 定义未实现过变量unrealized
    bool unrealized;
    {
        // 加锁
        runtimeLock.lock();
        // 获取非swift的Class
        result = getClassExceptSomeSwift(name);
        // 如果result有值,且result类的方法实现过了
        unrealized = result  &&  !result->isRealized();
        if (unrealized) {
            // 未实现的话,调用realizeClassMaybeSwiftAndUnlock实现一下,同时解锁
            result = realizeClassMaybeSwiftAndUnlock(result, runtimeLock);
            // runtimeLock is now unlocked
        } else {
            // 解锁
            runtimeLock.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;
}

说说isa

  • instance对象的isa指向class
    • 当调用对象方法时,通过instanceisa找到class,最后找到对象方法的实现进行调用
  • classisa指向meta-class
    • 当调用类方法时,通过classisa找到meta-class,最后找到类方法的实现进行调用
  • meta-classisa指向基类meta-class

说说superclass

  • classsuperclass指向meta-class

    • 如果没有父类,superclass指针为nil
  • meta-classsuperclass指向基类meta-class

    • 基类的meta-classsuperclass指向基类的class
  • 例子1(实例对象调用父类的对象方法),Student的instance实例调用Person的对象方法流程


    image.png
  • 例子2(类对象调用父类的类方法),Student的class对象调用Person类的类方法流程


    image.png

经典的isa和superclass图解

image.png
  • intance调用对象方法的轨迹

    • isa找到class,方法不存在,就通过superclass找父类,一直找,直到所有的父类找完都没有这个对象方法的实现时,再经过runtime的动态方法解析和消息转发,如果都没有,就会报unrecognized selector sent to instance 0xxxxxxxxx
  • class调用类方法的轨迹

    • isa找meta-class,方法不存在,就通过superclass找父类
    • 这种有一种特殊情况🙊,找到meta-class的都没有找到时,因为meta-classsuperclass指向基类meta-class的,所以会调用基类meta-class的类方法.这里不用奇怪明明调用的是类方法,最后却调用了基类的对象方法.
      • 示例代码如下
      #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;
      }
      

说说isa和superclass细节

  • 代码示意
// MJPerson类对象的地址:0x00000001000014c8
// MJPerson实例对象的isa:0x001d8001000014c9(为什么地址有差别,需要要与上一个 ISA_MASK 才是真实的值)
// isa & ISA_MASK:0x00000001000014c8

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

Class personClass = [MJPerson class];

Class personMetaClass = object_getClass(personClass);
  • 从64bit开始,isa需要进行一次位运算,才能计算出真实的isa地址值

    • isa & ISA_MASK
    • ISA_MASK的值


      image.png
  • superclass不需要做此操作,直接就是对应的地址值

isa和superclass-class和meta-class的结构

  • 代码结构
/// 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 {
        return bits.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;
};
  • 图片结构


    image.png

模拟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;  // instance对象占用的内存空间
#ifdef __LP64__
    uint32_t reserved;
#endif
    const uint8_t * ivarLayout;
    const char * name;  // 类名
    method_list_t * baseMethodList;
    protocol_list_t * baseProtocols;
    const ivar_list_t * ivars;  // 成员变量列表
    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;    // 方法列表
    property_list_t *properties;    // 属性列表
    const protocol_list_t * protocols;  // 协议列表
    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对象 */
struct mj_objc_object {
    void *isa;
};

/* 类对象 */
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;
}

面试题

  1. 对象的isa指针指向哪里?
    • instance对象的isa指向class对象
    • class对象的isa指向meta-class对象
    • meta-class对象的isa指向基类meta-class对象
  2. OC的类信息存放在哪里?
    • 对象方法,属性,成员变量,协议信息,存放在class对象中
    • 类方法,存放在meta-class对象中
    • 成员变量的具体值,存放在instance对象

相关文章

网友评论

    本文标题:iOS面试题-OC对象的isa指针

    本文链接:https://www.haomeiwen.com/subject/obeocltx.html