首页 > 基础资料 博客日记

Any metadata 的内存布局

2026-04-12 22:00:01基础资料围观1

本篇文章分享Any metadata 的内存布局,对你有帮助的话记得收藏一下,看极客资料网收获更多编程知识

Swiftenumstructclassprotocol都有对应的metadata

metadata用来描述上述这些类型本身,这点和OC的元类有点类似。

1 直观感受 metadata

假设用下面代码定义了一个enum:

enum Direction {
  case top
  case left
  case bottom
  case right
}

经过编译链接之后,上面的enum Direction以及对应的metadata都写入到了对应的Mach-O文件中。

假设编译链接之后的Mach-O文件名为A,那么,使用下面命令就可以查看enum Direction以及对应metadata的位置:

nm -n A | xcrun swift-demangle | grep -i Direction

nm 命令读取Mach-O中的所有符号信息。

swift-demangle对已经mangleswift符号名进行demangle

输出的结果如下:

000000000001e004 s _symbolic _____ 7iOSTest9DirectionO
0000000000020820 s full type metadata for iOSTest.Direction
0000000000020830 S type metadata for iOSTest.Direction

1列的数字代表对应项在Mach-O文件中的偏移。

输出的第1行的符号名7iOSTest9DirectionO有点怪。

这是因为swift-demangle没有彻底demangle干净,我们再次手动demangle下:

xcrun swift-demangle s7iOSTest9DirectionO

注意我们在符号名字前加了一个小写字母s

Swift会在mangle名前加一个字母s,代表Swift

再次demangle的输出结果为:

$s7iOSTest9DirectionO ---> iOSTest.Direction

可以看到,它正是enum Direction本身,位于0x1e004

输出第3行可以看到enum Directionmetadata位于0x20830

输出的第2行还有一个full type metadata,这是什么呢?

Swift里,一个类型的metadata前面根据类型的不同,会有一些其他数据。

其中任何类型都有一个数据是value witness table指针。

value witness tableSwift用来管理类型的生命周期,比如怎么复制、怎么销毁。

metadata

可以看到,所谓的full type metadata就是「其他数据」+对应的type metadata

而我们平时使用的.self语法,得到的就是metadata本身:

(lldb) p unsafeBitCast(Direction.self, to: UnsafeRawPointer.self)
(UnsafeRawPointer) 0x100adc830
(lldb) p/x 0x100adc830 - 0x0000000100abc000
(Int) 0x0000000000020830

从上面lldb的输出看到,经过ASLR的调整,Direction.self的位置正是0x20830

2 Any 的 metadata

Any是一个protocol,如下面输出:

(lldb) p Any.self
(Any.Protocol) Any

Any还是一个空协议组合,表示「没有任何要求」。

而「没有任何要求」是任何类型都满足的,这也是任何类型都能转成Any的原因。

2.1 Any metadata 的定义

Anymetadata定义为:

/// stdlib/public/runtime/metadata.cpp
/// The standard metadata for Any.
const FullMetadata<ExistentialTypeMetadata> swift::
METADATA_SYM(ANY_MANGLING) = {
  { &OpaqueExistentialValueWitnesses_0 }, // ValueWitnesses
  ExistentialTypeMetadata(
    ExistentialTypeFlags() // Flags
      .withNumWitnessTables(0)
      .withClassConstraint(ProtocolClassConstraint::Any)
      .withHasSuperclass(false)
      .withSpecialProtocol(SpecialProtocol::None)),
};

上面的定义准确的说,是Anyfull metadata

定义中的FullMetadata是一个C++结构体,定义为:

/// swift/ABI/metadata.h

template <class T> struct FullMetadata : T::HeaderType, T {
  ...
};

其中模版参数T = ExistentialTypeMetadata

我们手动代入后,FullMetadata的完整定义为:

struct FullMetadata : ExistentialTypeMetadata::HeaderType, ExistentialMetadata {
  ...
};

从上面代码可以看到,FullMetadata是多重继承。

一部分继承自ExistentialTypeMetadata::HeaderType

一部分继承ExistentialTypeMetadata

要想弄清楚ExistentialTypeMetadata::HeaderType,首先得看下ExistentialTypeMetadata的定义:

/// stdlib/public/runtime/metadata.cpp

using ExistentialTypeMetadata
  = TargetExistentialTypeMetadata<InProcess>;

从代码上可以看到,ExistentialTypeMetadata实际上是TargetExistentialTypeMetadata<InProcess>

接着看TargetExistentialTypeMetadata的定义:

/// swift/ABI/metadata.h

template <typename Runtime>
struct TargetExistentialTypeMetadata
  : TargetMetadata<Runtime>,
    swift::ABI::TrailingObjects<
      TargetExistentialTypeMetadata<Runtime>,
      ConstTargetMetadataPointer<Runtime, TargetMetadata>,
      TargetProtocolDescriptorRef<Runtime>> {
  using HeaderType = TargetTypeMetadataHeaderBase<Runtime>;
  ...

public:
  using StoredPointer = typename Runtime::StoredPointer;
  /// The number of witness tables and class-constrained-ness of the type.
  ExistentialTypeFlags Flags;

  /// The number of protocols.
  uint32_t NumProtocols;

  ...
};

从代码里可以看到,TargetExistentialTypeMetadata同样是多重继承。

一部分继承自TargetMetadata

一部分继承自swift::ABI::TrailingObjects

swift::ABI::TrailingObjects结构体没有任何实例变量,可以先不用关心。

swift::ABI::TrailingObjects的定义如下:

/// swift/ABI/TrailingObjects.h

emplate <typename BaseTy, typename... TrailingTys>
class swift_ptrauth_struct_derived(BaseTy) TrailingObjects
    : private trailing_objects_internal::TrailingObjectsImpl<
                            trailing_objects_internal::AlignmentCalcHelper<
                                TrailingTys...>::Alignment,
                            BaseTy, TrailingObjects<BaseTy, TrailingTys...>,
                            BaseTy, TrailingTys...> {
  ...
};

我们继续回到TargetExistentialTypeMetadata结构体上来。

TargetExistentialTypeMetadata内部定义了HeaderType:

using HeaderType = TargetTypeMetadataHeaderBase<Runtime>;

从定义上看,TargetExistentialTypeMetadata::HeaderType实际上是TargetTypeMetadataHeaderBase

呜~~~

现在,FullMetadata完整定义可以重写为:

struct FullMetadata : TargetTypeMetadataHeaderBase, TargetExistentialTypeMetadata {
  ...
};

FullMetadata

2.2 内存布局

有了继承关系,就可以画出full metadata的内存布局。

内存布局

2.2.1 ValueWitness

ValueWitness就是value witness table指针,8字节。

2.2.2 Kind

Kind 是一个标识符,代表了当前是什么类型,8字节。

Kind的定义为:

/// swift/ABI/MetadataValues.h

enum class MetadataKind : uint32_t {
#define METADATAKIND(name, value) name = value,
#define ABSTRACTMETADATAKIND(name, start, end)                                 \
  name##_Start = start, name##_End = end,
#include "MetadataKind.def"
  LastEnumerated = 0x7FF,
};

注意看,这里的MetadataKind32bit的,而Kind本身是8字节的。

之所以这么设计,是因为如果一个class可以与OC交互,此时Kind要当成isa指针看待。

完整的Kind定义在文件MetadaKind.def中。

常见的Kind如下:

// 纯 Swift 类
class - 0x0
struct - 0x200
enum - 0x201
optional - 0x202
opaqual - 0x300
tuple - 0x301
existential - 0x303

2.2.3 Flags

Flags是一个C++类,定义如下:

/// swift/ABI/MetadataValues.h

class ExistentialTypeFlags {
public:
  typedef uint32_t int_type;

private:
  enum : int_type {
    NumWitnessTablesMask  = 0x00FFFFFFU,
    ClassConstraintMask   = 0x80000000U, // Warning: Set if NOT class-constrained!
    HasSuperclassMask     = 0x40000000U,
    SpecialProtocolMask   = 0x3F000000U,
    SpecialProtocolShift  = 24U,
  };
  int_type Data;

它里面只有一个实例变量Data,占用4字节32bit

2.3.4 numOfProtocols

numOfProtocols占用4字节。


文章来源:https://www.cnblogs.com/chaoguo1234/p/19856295
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:jacktools123@163.com进行投诉反馈,一经查实,立即删除!

标签:

相关文章

本站推荐

标签云