1.类的关系探究
- 类是否是具有唯一性
Class class1 = [KBSon class];
Class class2 = [KBSon alloc].class;
Class class3 = object_getClass([KBSon alloc]);
Class class4 = [KBSon class];
NSLog(@"\n%p-\n%p-\n%p-\n%p",class1,class2,class3,class4);
复制代码
打印后地址都是 0x100008400-0x100008400-0x100008400-0x100008400
说明在我们内存中KBSon
应该是唯一的,类似我们开发中 单例
的概念。
- 类的继承
我们在iOS开发中经常使用继承,子类可以使用父类的方法和属性。
KBSon *son = [KBSon alloc];
KBFather *father = [KBFather alloc];
NSLog(@"%@-%@",son,father);
NSLog(@"%@",class_getSuperclass(son.class));
NSLog(@"%@",class_getSuperclass(father.class));
NSLog(@"%@",class_getSuperclass(NSObject.class));
复制代码
打印后发现KBSon -> KBFather -> NSObject -> null
它们的继承关系如下,
说明 NSObject
就是最根本的类没有父类,类似 亚当和夏娃
。在oc中所有的类都是继承于NSObject
- isa走向
我们在新的方法中探究下 son
的isa走位
KBSon *son = [[KBSon alloc]init];
Class selfClass = object_getClass(son.class);
Class superClass = object_getClass(selfClass);
Class superSuperClass = object_getClass(superClass);
Class rootClass = object_getClass(NSObject.class);
NSLog(@"\n%@-%p 实例对象\n%@-%p 类\n%@-%p 元类\n%@-%p 根元类\n%@-%p 根根元类",son,son,selfClass,selfClass,superClass,superClass,superSuperClass,superSuperClass,rootClass,rootClass);
复制代码
打印发现
<KBSon: 0x10067f800>-0x10067f800 实例对象
KBSon-0x1000083f0 类
NSObject-0x7fff88b97ca0 元类
NSObject-0x7fff88b97ca0 根元类
NSObject-0x7fff88b97ca0 根根元类
复制代码
对应的关系如下
- 元类的继承
我们继续看下son
的 superClass
Class sMetaClass = object_getClass(KBSon.class);
Class sSuperMetaClass = class_getSuperclass(sMetaClass);
Class sRootMetaClass = class_getSuperclass(sSuperMetaClass);
Class sRootSuperMetaClass = class_getSuperclass(sRootMetaClass);
Class lastMetaClass = class_getSuperclass(sRootSuperMetaClass);
NSLog(@"%@ - %@ - %@-%@- %@",sMetaClass,sSuperMetaClass,sRootMetaClass,sRootSuperMetaClass,lastMetaClass);
复制代码
打印后
2021-06-18 15:40:02.117330+0800 类的原理分析[36495:1702498] KBSon - KBFather - NSObject-NSObject- (null)
复制代码
他们的关系如下
最终得到了isa和继承关系
2.类的构成探究
2.1 类的源码分析
之前我们使用终端使用Clang
编译器命令编译 探究我们我们知道类是类似
struct NSObject_IMPL {
Class isa;
};
复制代码
isa 就是类的地址。我们通过源码分析可知
结构体中包含一个isa指针,指向 object_class
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
复制代码
OBJC2_UNAVAILABLE
表明2.0以后在使用,废弃了。使用新的struct objc_class
在objc-runtime-new
中查看
struct objc_class : objc_object {
objc_class(const objc_class&) = delete;
objc_class(objc_class&&) = delete;
void operator=(const objc_class&) = delete;
void operator=(objc_class&&) = delete;
// 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
...下面有一些方法具体参考源码
}
复制代码
struct objc_object {
private:
isa_t isa;
public:
// ISA() assumes this is NOT a tagged pointer object
Class ISA(bool authenticated = false);
// rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
Class rawISA();
// getIsa() allows this to be a tagged pointer object
Class getIsa();
uintptr_t isaBits() const;
...下面有一些方法具体参考源码
}
复制代码
ISA
:继承自objc_object,指向元类。superclass
:指向父类。cache
:方法缓存,当调用一次方法后就会缓存进vtable中,加速下次调用。bits
:具体类信息(成员变量、属性、方法)。
bits
是class_rw_t
类型的
我们通过源码得到
struct class_rw_t {
.....不关注.....
const method_array_t methods() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->methods;
} else {
return method_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseMethods()};
}
}
const property_array_t properties() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->properties;
} else {
return property_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProperties};
}
}
const protocol_array_t protocols() const {
auto v = get_ro_or_rwe();
if (v.is<class_rw_ext_t *>()) {
return v.get<class_rw_ext_t *>(&ro_or_rw_ext)->protocols;
} else {
return protocol_array_t{v.get<const class_ro_t *>(&ro_or_rw_ext)->baseProtocols};
}
}
}
复制代码
封装了获取 methods
、properties
、 protocols
的方法。我能怎么获取呢?
2.2 类的内存组成
struct cache_t {
private:
explicit_atomic<uintptr_t> _bucketsAndMaybeMask; //uintptr_t 8字节
union {
struct {
explicit_atomic<mask_t> _maybeMask; // mask_t 4字节
#if __LP64__
uint16_t _flags; // 2 字节
#endif
uint16_t _occupied; // 2 字节
};
explicit_atomic<preopt_cache_t *> _originalPreoptCache;
};
}
复制代码
是由2部分组成 _bucketsAndMaybeMask
和union
联合体中有一个 _originalPreoptCache
(指针 8字节)与结构体( _originalPreoptCache
与结构体只需要计算一个,共用一块内存)。所以cache_t大小为 16
字节。
2.3 lldb分析
2.3.1 isa 打印验证
通过打印可以验证之前isa指针指向
2.3.2 继承 打印验证
打印发现父类的指针地址确实指向父类,NSObject 的父类nil
2.3.3 元类 继承验证
通过打印类的内存地址找到元类,用元类的内存地址打印找到父元类,之后继续查找到nsobject ,nsobject的父类为空
2.3.4 属性 打印验证
通过控制台进行打印。
- 前面得到了想要获取
bites
要进行首位置偏移8+8+16个单位 - 拿到后进行强转
class_data_bits_t *
- 调用
data()
函数获取class_rw_t
指针 - 打印
class_rw_t
我们要获取属性
通过properties获取到的属性是一个property_array_t,结构如下:
class property_array_t :
public list_array_tt<property_t, property_list_t, RawPtr>
{
typedef list_array_tt<property_t, property_list_t, RawPtr> Super;
public:
property_array_t() : Super() { }
property_array_t(property_list_t *l) : Super(l) { }
};
复制代码
通过访问 ptr
能够获取到 property_list_t
数组:
$4
就相当于迭代器了,property_list_t源码结构如下:
struct property_list_t : entsize_list_tt<property_t, property_list_t, 0> {
};
复制代码
最终通过get方法获取属性但是没有 成员变量
说明成员变量不存在这。
2.3.4 方法 打印验证
上面我们知道class_rw_t
中有属性列表还有methods
同样的方式打印方法
一共8
个方法,3个属性的set
和get
方法 共6个,一个自定义的实例方法
,一个 alloc
方法,没有 类方法
(是method_t
中有一个结构体big
中有name和imp,并且提供了一个big方法,所以可以通过big
方法获取)
struct method_t {
//......
struct big {
SEL name;
const char *types;
MethodListIMP imp;
};
big &big() const {
ASSERT(!isSmall());
return *(struct big *)this;
}
//......
};
复制代码
2.3.4 成员变量 打印验证
在properties
中并没有找到成员变量,但是在class_rw_t
的methods()附近发现了ro()
,它返回class_ro_t
类型:
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;
};
复制代码
ivar_list_t
类型:
struct ivar_list_t : entsize_list_tt<ivar_t, ivar_list_t, 0> {
bool containsIvar(Ivar ivar) const {
return (ivar >= (Ivar)&*begin() && ivar < (Ivar)&*end());
}
};
复制代码
一共6个属性,完整的得到了
2.3.4 类方法 打印验证
在之前的methods()
中并没有找到类方法,根据isa的走位直接进去元类查看。元类的结构也是objc_class
所以可以尝试同样的方式获取。
都是objc_class
所以可以用相同的方式进行获取,最终得到了类方法。
题外:实例对象包含成员变量的具体值
@implementation LGTeacher
- (instancetype)init{
self = [super init];
if (self) {
self.nickName = @"父亲";
[self saySomething];
return self;
}
return nil;
}
复制代码
总结:
instance 对象(实例对象):成员变量的具体值、isa 指针
复制代码
class 对象(类对象):对象方法、属性、成员变量、协议信息、isa 指针、superclass 指针
复制代码
meta-class 对象(元类对象,即描述对象的对象,元数据是指描述数据的数据):类方法、isa 指针、superclass 指针。
复制代码
我通过源码分析和实际的lldb打印有了更深的理解,对于isa走位图理解更深刻了。