OC底层03:isa分析

每一个Objective-C对象都与一个isa指针绑定,那这个isa又是如何与对象进行绑定的呢。

clang

要知道如何绑定的,我们需要先知道OC类在底层文件的定义,这就需要用到clang将对应文件编译成cpp文件
可以通过命令clang -rewrite-objc xx.m -o xx.cpp

  1. 先创建一个LGPerson类,添加属性name
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation LGPerson
@end
  1. 然后再main函数中调用
LGPerson *objc1 = [LGPerson alloc];
  1. 在终端cd到对应文件夹,并输入对应命令


  2. 可以在对应文件夹找到cpp文件


  3. 我们可以找到LGPerson的底层实现
#ifndef _REWRITER_typedef_LGPerson
#define _REWRITER_typedef_LGPerson
typedef struct objc_object LGPerson;
typedef struct {} _objc_exc_LGPerson;
#endif

extern "C" unsigned long OBJC_IVAR_$_LGPerson$_name;
struct LGPerson_IMPL {
    struct NSObject_IMPL NSObject_IVARS;
    NSString *_name;
};

// @property (nonatomic, copy) NSString *name;
/* @end */

// @implementation LGPerson


static NSString * _I_LGPerson_name(LGPerson * self, SEL _cmd) { return (*(NSString **)((char *)self + OBJC_IVAR_$_LGPerson$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_LGPerson_setName_(LGPerson * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct LGPerson, _name), (id)name, 0, 1); }
// @end
  1. 可见在C中,OC类的实现为结构体,struct NSObject_IMPL NSObject_IVARS;为伪继承,NSObject_IVARS也就是isa指针
struct NSObject_IMPL {
    Class isa;
};

联合体

在alloc底层分析中,知道isa的绑定是在initInstanceIsa()中执行

inline void 
objc_object::initInstanceIsa(Class cls, bool hasCxxDtor)
{
    ASSERT(!cls->instancesRequireRawIsa());
    ASSERT(hasCxxDtor == cls->hasCxxDtor());

    initIsa(cls, true, hasCxxDtor);
}

inline void 
objc_object::initIsa(Class cls, bool nonpointer, bool hasCxxDtor) 
{ 
    ASSERT(!isTaggedPointer()); 
    
    if (!nonpointer) {
        isa = isa_t((uintptr_t)cls);
    } else {
        ASSERT(!DisableNonpointerIsa);
        ASSERT(!cls->instancesRequireRawIsa());

        isa_t newisa(0);

#if SUPPORT_INDEXED_ISA
        ASSERT(cls->classArrayIndex() > 0);
        newisa.bits = ISA_INDEX_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.indexcls = (uintptr_t)cls->classArrayIndex();
#else
        newisa.bits = ISA_MAGIC_VALUE;
        // isa.magic is part of ISA_MAGIC_VALUE
        // isa.nonpointer is part of ISA_MAGIC_VALUE
        newisa.has_cxx_dtor = hasCxxDtor;
        newisa.shiftcls = (uintptr_t)cls >> 3;
#endif

        // This write must be performed in a single store in some cases
        // (for example when realizing a class because other threads
        // may simultaneously try to use the class).
        // fixme use atomics here to guarantee single-store and to
        // guarantee memory order w.r.t. the class index table
        // ...but not too atomic because we don't want to hurt instantiation
        isa = newisa;
    }
}

isa是一个isa_t的结构,查看isa_t的源码,isa_t是一个联合体

union isa_t {
    isa_t() { }
    isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

什么是联合体

结构体中,会为每个对象分配好空间,比如一个int占4个字节,但是实际使用中,只使用了2个字节,这样就浪费了2个字节,而联合体就是为了解决这个问题,节约空间而提出的。

isa_t的结构

ISA_BITFIELD分配bits对应的位域空间所使用的内存(每个位置作用,见注释)

# if __arm64__ //iOS平台下
#   define ISA_MASK        0x0000000ffffffff8ULL
#   define ISA_MAGIC_MASK  0x000003f000000001ULL
#   define ISA_MAGIC_VALUE 0x000001a000000001ULL
#   define ISA_BITFIELD                                                      \
      uintptr_t nonpointer        : 1;                                       \        //是否包含类信息,0表示纯isa指针
      uintptr_t has_assoc         : 1;                                       \       //是否有关联的对象
      uintptr_t has_cxx_dtor      : 1;                                       \       //是否有析构函数,类似于OC中的dealloc
      uintptr_t shiftcls          : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \   //类信息
      uintptr_t magic             : 6;                                       \      //分辨对象是否初始化
      uintptr_t weakly_referenced : 1;                                       \   //是否为弱引用对象
      uintptr_t deallocating      : 1;                                       \    //是否正在释放内存
      uintptr_t has_sidetable_rc  : 1;                                       \   //当对象引用计数大于 10 时,则需要借用该变量存储进位
      uintptr_t extra_rc          : 19                                 // 对象的引用计数值
#   define RC_ONE   (1ULL<<45)
#   define RC_HALF  (1ULL<<18)

# elif __x86_64__ //Mac系统下
#   define ISA_MASK        0x00007ffffffffff8ULL
#   define ISA_MAGIC_MASK  0x001f800000000001ULL
#   define ISA_MAGIC_VALUE 0x001d800000000001ULL
#   define ISA_BITFIELD                                                        \
      uintptr_t nonpointer        : 1;                                         \
      uintptr_t has_assoc         : 1;                                         \
      uintptr_t has_cxx_dtor      : 1;                                         \
      uintptr_t shiftcls          : 44; /*MACH_VM_MAX_ADDRESS 0x7fffffe00000*/ \
      uintptr_t magic             : 6;                                         \
      uintptr_t weakly_referenced : 1;                                         \
      uintptr_t deallocating      : 1;                                         \
      uintptr_t has_sidetable_rc  : 1;                                         \
      uintptr_t extra_rc          : 8
#   define RC_ONE   (1ULL<<56)
#   define RC_HALF  (1ULL<<7)

bitsunsigned long类型,占用64位,位域内存中的结构如下:


initIsa() 进行一系列赋值后,将newisa赋值给isa实现绑定。newisa.shiftcls = (uintptr_t)cls >> 3;类信息就保存在shiftcls中。

通过isa找对应对象

我们了解了isa绑定的方式,我们就以通过isa找到原有的类。

方法一:第一种方法,直接获取shiftcls中的信息

  1. 去掉nonpointer,has_assoc,has_cxx_dtor信息 :p (uintptr_t)0x001d80010000218d >> 3
  2. 去掉shiftcls后面的信息:p (uintptr_t) $5 << 20
  3. 还原shiftcls到原有位置:p (uintptr_t) $6 >> 17

方法二:直接通过苹果提供的ISA_MASK进行位运算

你可能感兴趣的:(OC底层03:isa分析)