抱歉,标题党了😆😆,本文主要是笔者的一些探究,分享记录一下
思考
对于OC的实例对象、类对象、元类对象都很熟悉了。那么swift的对象呢?是否跟OC一样有这么多种对象?
温故
我们都知道OC中有3种对象,分别是实例对象、类对象、元类对象,数据结构如下:
typedef struct objc_class *Class;
typedef struct objc_object *id;
struct objc_object {
Class isa;
}
// 类对象、元类对象
struct objc_class : objc_object {
Class superclass;
cache_t cache;
class_data_bits_t bits;
}
实例化一个继承自NSObject的类对象,这个实例对象的第一个成员变量就是Class isa
@interface NSObject <NSObject> {
Class isa;
}
struct 实例对象名 {
Class isa;
其它成员变量
}
实例对象中的isa指针,存的是指向该实例对象的类对象的Class地址,通过这个地址就可以找到这个类对象的类信息(实例方法,协议,属性信息、成员变量信息等)。而类对象中也有个isa,存的是指向该类对象的元类对象的地址,同样,通过这个地址就可以找到元类对象的类信息(类方法等)
以上老生常谈的OC对象的一些核心的信息。
那么swift中又是如何的呢?
知新
先来一个栗子:
class Animal: NSObject {
@objc func getCat() -> AnyObject {
let cat = Cat()
cat.run()
return cat
}
}
class Cat {
var age: Int = 10
@objc var name: String = "Tom"
@objc class func objcClassFun() {
print(#function)
}
class func classFun() {
print(#function)
}
func run() {
print(#function)
}
@objc func eat() {
print(#function)
}
@objc dynamic func look() {
print(#function)
}
func hello() {
print(#function)
}
}
Animal *animal = [[Animal alloc] init];
id cat = [animal getCat];
[cat performSelector:@selector(eat)];
这样是否能够调用到eat方法? 答案是可以
是不是很意外,我们先来回顾下performSelector 是如何调用方法的,源码如下:
- (id)performSelector:(SEL)sel {
if (!sel) [self doesNotRecognizeSelector:sel];
return ((id(*)(id, SEL))objc_msgSend)(self, sel);
}
objc_msgSend 第一个参数是self,也就是cat对象。我们都知道objc_msgSend 是通过isa来找到实例方法的,因为Cat是纯的swfit类,那么也就是说Cat类至少是有跟OC类对象相似的结构的。
于是带着这样的疑问,在objc的源码中找到了这样一个结构:
struct swift_class_t : objc_class {
uint32_t flags;
uint32_t instanceAddressOffset;
uint32_t instanceSize;
uint16_t instanceAlignMask;
uint16_t reserved;
uint32_t classSize;
uint32_t classAddressOffset;
void *description;
// ...
void *baseAddress() {
return (void *)((uint8_t *)this - classAddressOffset);
}
};
从源码中可以看出,swift_class_t 继承了objc_class,如果真的是这样,前面说的能够调到eat方法,就成立了。
但这仅仅只是猜测,需要进一步验证。
从汇编代码可以看到,Cat最终调用了swift_allocObject方法。
static HeapObject *_swift_allocObject_(HeapMetadata const *metadata,
size_t requiredSize,
size_t requiredAlignmentMask) {
assert(isAlignmentMask(requiredAlignmentMask));
auto object = reinterpret_cast<HeapObject *>(
swift_slowAlloc(requiredSize, requiredAlignmentMask));
// NOTE: this relies on the C++17 guaranteed semantics of no null-pointer
// check on the placement new allocator which we have observed on Windows,
// Linux, and macOS.
new (object) HeapObject(metadata);
// If leak tracking is enabled, start tracking this object.
SWIFT_LEAKS_START_TRACKING_OBJECT(object);
SWIFT_RT_TRACK_INVOCATION(object, swift_allocObject);
return object;
}
透过swift的源码可以看到,最终生成是一个HeapObject的对象,数据结构如下:
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
/// The Swift heap-object header.
/// This must match RefCountedStructTy in IRGen.
struct HeapObject {
/// This is always a valid pointer to a metadata object.
HeapMetadata const *metadata;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}
HeapMetadata的数据结构如下:
struct TargetMetadata {
StoredPointer Kind; // 指针
}
struct TargetHeapMetadata: TargetMetadata {
// 没有成员
}
struct TargetAnyClassMetadata: TargetHeapMetadata {
ConstTargetMetadataPointer<Runtime, swift::TargetClassMetadata> Superclass;
TargetPointer<Runtime, void> CacheData[2];
StoredSize Data;
}
// 所以 TargetAnyClassMetadata 的结构如下
struct TargetAnyClassMetadata {
StoredPointer Kind; // isa
ConstTargetMetadataPointer<Runtime, swift::TargetClassMetadata> Superclass;
TargetPointer<Runtime, void> CacheData[2];
StoredSize Data;
}
struct TargetClassMetadata : public TargetAnyClassMetadata {
/// Swift-specific class flags.
ClassFlags Flags;
/// The address point of instances of this type.
uint32_t InstanceAddressPoint;
/// The required size of instances of this type.
/// 'InstanceAddressPoint' bytes go before the address point;
/// 'InstanceSize - InstanceAddressPoint' bytes go after it.
uint32_t InstanceSize;
/// The alignment mask of the address point of instances of this type.
uint16_t InstanceAlignMask;
/// Reserved for runtime use.
uint16_t Reserved;
/// The total size of the class object, including prefix and suffix
/// extents.
uint32_t ClassSize;
/// The offset of the address point within the class object.
uint32_t ClassAddressPoint;
/// An out-of-line Swift-specific description of the type, or null
/// if this is an artificial subclass. We currently provide no
/// supported mechanism for making a non-artificial subclass
/// dynamically.
TargetSignedPointer<Runtime, const TargetClassDescriptor<Runtime> * __ptrauth_swift_type_descriptor> Description;
/// A function for destroying instance variables, used to clean up after an
/// early return from a constructor. If null, no clean up will be performed
/// and all ivars must be trivial.
TargetSignedPointer<Runtime, ClassIVarDestroyer * __ptrauth_swift_heap_object_destructor> IVarDestroyer;
}
这个结构有没有熟悉,跟上面swift_class_t 是一致的。
虽然最终生成的HeapObject对象,但最终暴露出去给OC层面的其实是另外一个类SwiftObject
#define SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS \
InlineRefCounts refCounts
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
@private
Class isa;
SWIFT_HEAPOBJECT_NON_OBJC_MEMBERS;
}
可以看到,第一个成员变量的类型就是Class类型,也就是说NSObject和HeapObject是可以通过指定类型来进行类型转换的。
这一点可以通过lldb调试器来窥探到,如下图:
明显可以看到,cat是继承了一个叫_TtCs12_SwiftObject的类,这也恰好印证我们上面的推测。并且通过object_getClass这个方法也可以证明到这一点。
既然类对象如此,那么是否存在元类对象呢?答案是肯定的。通过object_getClass来获取类对象的类对象的方法,同样的是可以获取到类方法,并且成功调用的。
总结
至此,也就说明为什么一个纯Swift类可以通过OC的objc_msgSend的方式来调用了。因为Swift类保留了OC类的数据结构,只是在OC类的基础上,增加了一些新的信息。当然这样说并不是很严谨,但不妨碍我们去理解。
未完待续
如果调用的是run()方法,是否能调用成功?下篇会再继续探究,@objc到底做了什么?