每一个Objective-C对象都与一个isa指针绑定,那这个isa又是如何与对象进行绑定的呢。
clang
要知道如何绑定的,我们需要先知道OC类在底层文件的定义,这就需要用到clang将对应文件编译成cpp文件
可以通过命令clang -rewrite-objc xx.m -o xx.cpp
- 先创建一个LGPerson类,添加属性name
@interface LGPerson : NSObject
@property (nonatomic, copy) NSString *name;
@end
@implementation LGPerson
@end
- 然后再main函数中调用
LGPerson *objc1 = [LGPerson alloc];
-
在终端cd到对应文件夹,并输入对应命令
-
可以在对应文件夹找到cpp文件
- 我们可以找到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
- 可见在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)
bits
为unsigned long
类型,占用64位,位域内存中的结构如下:
在
initIsa()
进行一系列赋值后,将newisa
赋值给isa实现绑定。newisa.shiftcls = (uintptr_t)cls >> 3;
类信息就保存在shiftcls
中。
通过isa找对应对象
我们了解了isa绑定的方式,我们就以通过isa找到原有的类。
方法一:第一种方法,直接获取shiftcls
中的信息
- 去掉
nonpointer
,has_assoc
,has_cxx_dtor
信息 :p (uintptr_t)0x001d80010000218d >> 3
- 去掉
shiftcls
后面的信息:p (uintptr_t) $5 << 20
- 还原
shiftcls
到原有位置:p (uintptr_t) $6 >> 17