isa的底层结构
isa 是一个共用体, 共用体占用的内存大小, 跟它占据内存最大的成员变量相同, (isa一共是8字节)
在我们现在使用的arm64/arm64e的机器上,isa使用了共同体, 搭配位域, 来更合理的使用内存空间, isa存储的内容中, 除了对应class的地址外, 还存着其他的信息:
arm64e: arm64e 架构用于 A12 芯片组, iphoneX以上的机子都是arm64e
uintptr_t nonpointer: 是否使用了isa优化技术, 1 代表有, 0 代表没有
uintptr_t has_assoc: 是否使用过关联 , 1 代表有, 0 代表没有 (0的话, dealloc的时候会更快)
uintptr_t weakly_referenced: 是否被若指针引用过, 1 代表有, 0 代表没有 (0的话, dealloc的时候会更快)
uintptr_t shiftcls / shiftcls_and_sig: class的地址
uintptr_t extra_rc: 引用计数
uintptr_t has_sidetable_rc: retain count是否超过extra_rc能放的空间, 1代表超过, 0代表没有
uintptr_t magic: 是否初始化成功(objc_object::initIsa中能看到,跟nonpointer一起赋的值)
uintptr_t has_cxx_dtor: 看起来像是是否有cpp析构函数(不知道啥玩意)
union isa_t {
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
uintptr_t bits;
private:
// Accessing the class requires custom ptrauth operations, so
// force clients to go through setClass/getClass by making this
// private.
Class cls;
public:
#if defined(ISA_BITFIELD)
struct {
ISA_BITFIELD; // defined in isa.h
};
bool isDeallocating() {
return extra_rc == 0 && has_sidetable_rc == 0;
}
void setDeallocating() {
extra_rc = 0;
has_sidetable_rc = 0;
}
#endif
void setClass(Class cls, objc_object *obj);
Class getClass(bool authenticated);
Class getDecodedClass(bool authenticated);
};
// 64e
# if __has_feature(ptrauth_calls) || TARGET_OS_SIMULATOR
# define ISA_MASK 0x007ffffffffffff8ULL
# define ISA_MAGIC_MASK 0x0000000000000001ULL
# define ISA_MAGIC_VALUE 0x0000000000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 0
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t weakly_referenced : 1; \
uintptr_t shiftcls_and_sig : 52; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 8
# define RC_ONE (1ULL<<56)
# define RC_HALF (1ULL<<7)
# else
# define ISA_MASK 0x0000000ffffffff8ULL
# define ISA_MAGIC_MASK 0x000003f000000001ULL
# define ISA_MAGIC_VALUE 0x000001a000000001ULL
# define ISA_HAS_CXX_DTOR_BIT 1
# define ISA_BITFIELD \
uintptr_t nonpointer : 1; \
uintptr_t has_assoc : 1; \
uintptr_t has_cxx_dtor : 1; \
uintptr_t shiftcls : 33; /*MACH_VM_MAX_ADDRESS 0x1000000000*/ \
uintptr_t magic : 6; \
uintptr_t weakly_referenced : 1; \
uintptr_t unused : 1; \
uintptr_t has_sidetable_rc : 1; \
uintptr_t extra_rc : 19
# define RC_ONE (1ULL<<45)
# define RC_HALF (1ULL<<18)
# endif
位域是什么
- 为了节省空间, 结构体可以指定成员变量占据的位数, 当然占据的位数不能超过成员变量的类型最大可以占据的位数
- 最开始的成员变量, 从最右边开始占据跟位数相等的空间
- 利用位操作, 利用 & 操作, 就可以获取到每一位 或者 相邻若干位 的值
- 利用位操作, 利用 | 操作, 就可以设置每一位的值
描述一下对象,类,元类的isa指针指向
对象 -> 类 -> 元类 -> 根元类 -> 自己
类A只有+test的声明, 但没有实现+test方法, 如果NSObject实现了-test方法,当执行[A test]时会发生什么
会调用到NSObject的-test方法中去, 因为类A的isa指针指向类A的元类, 根据消息发送机制, 会沿着类A的元类的superClass一路往上走, 因为根元类的父类是NSObject, 所以会执行NSObject的-test方法
[[NSObject class] class] 返回的是NSObject的类对象, 还是NSObject的元类对象
是类对象, 因为类对象调用的+class方法, 返回是self
对象的isa指针是存着类对象的地址吗
不一定是, 64bit以后, 实例对象的isa的值, 需要&ISA_MASK后, 才是类对象所在的地址
核心图
DEMO
// xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main_arm64.cpp
#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <malloc/malloc.h>
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSObject *obj = [NSObject new];
Class cls1 = [obj class];
Class cls2 = object_getClass(obj);
Class cls3 = [NSObject class];
Class cls4 = [[NSObject class] class]; // 这种还是类
Class cls5 = objc_getClass("NSObject"); // 有一个hashmap, 根据传入的名称查找类
Class metaCls1 = object_getClass(cls1); // 查isa指针的指向
Class metaCls2 = objc_getMetaClass("NSObject"); // 有一个hashmap, 根据传入的名称查找元类
bool isMeta = class_isMetaClass(metaCls1);
NSLog(@"%p %p %p %p %p", cls1, cls2, cls3, cls4, cls5);
NSLog(@"%p %p", metaCls1, metaCls2);
NSLog(@"%d", isMeta);
}
return 0;
}
// 2021-09-27 14:43:48.238769+0800 TestOC[51231:5399709] 0x7fff88960cc8 0x7fff88960cc8 0x7fff88960cc8 0x7fff88960cc8 0x7fff88960cc8
// 2021-09-27 14:43:48.239037+0800 TestOC[51231:5399709] 0x7fff88960ca0 0x7fff88960ca0
// 2021-09-27 14:43:48.239097+0800 TestOC[51231:5399709] 1
/* NSObject的class 方法实现
NSObject.mm {
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
}
*/
//Class object_getClass(id obj)
//{
// if (obj) return obj->getIsa();
// else return Nil;
//}
/***********************************************************************
* objc_getClass. Return the id of the named class. If the class does
* not exist, call _objc_classLoader and then objc_classHandler, either of
* which may create a new class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
//Class objc_getClass(const char *aClassName)
//{
// if (!aClassName) return Nil;
//
// // NO unconnected, YES class handler
// return look_up_class(aClassName, NO, YES);
//}