[英]In Swift, how to get the true size of an `Any` variable?
我希望能够获取 Swift 中Any
变量的基础数据类型的大小。我希望通过运行MemoryLayout.size(ofValue: anyObject)
可以实现这一点,但是该表达式始终返回32
,而不管基础Any
object 的数据类型。我假设32
是内部Any
构造/类型的大小,它包含有关它存储的 object 的元数据。
如何获取基础数据类型的大小?
let regularInt: Int = 1
let anyInt: Any = Int(2) as Any
MemoryLayout<Int>.size // 4
MemoryLayout<type(of: anyInt)>.size // Can't do that
MemoryLayout.size(ofValue: regularInt) // 4
MemoryLayout.size(ofValue: anyInt) // 32
// How do I get size "4" out of `anyInt`?
我将从一些关于Any
在这种情况下的局限性的技术细节开始。
那么,什么是Any
? 它是一个空协议,每个类型都隐含地遵守它。
编译器如何表示协议类型的变量? 它是通过将实际值包装在一个存在的容器中。 所以基本上,当您引用此类变量时,您实际上是在与容器对话(好吧,实际上不是您,而是编译器:)。
存在容器的布局可以表示为 C 结构:
struct OpaqueExistentialContainer {
void *fixedSizeBuffer[3];
Metadata *type;
WitnessTable *witnessTables[NUM_WITNESS_TABLES];
};
容器元素在本文档中得到了很好的解释,我也将在这里尝试对其进行总结:
fixedSizeBuffer
要么保存整个值,如果它少于 24 个字节,要么保存指向堆分配区域的指针,其中包含该值type
是指向类型元数据的指针witnessTables
是使此布局占据各种大小的原因,因为协议见证表的数量可以从零到几乎任意数量的协议不等。所以,考虑到以上几点:
Any
不需要 witness 表,因此它占用 32 个字节请注意,如果不涉及 class 协议,则上述内容成立,如果涉及 class 协议,则现有容器布局会稍微简化一些,这在上面的链接文档中也有更好的描述。
现在,回到问题的问题,它是编译器创建的存在容器,它阻止您访问实际类型。 编译器不提供此结构,并且透明地将任何对协议要求的调用转换为通过存储在容器中的见证表进行分派。
但是,我可以问你,你为什么要传播Any
? 我假设您不想以通用方式处理所有可能的和未来的类型。 标记协议可能在这里有所帮助:
protocol MemoryLayouted { }
extension MemoryLayouted {
var memoryLayoutSize: Int { MemoryLayout.size(ofValue: self) }
}
然后你剩下要做的就是为你想要支持的类型添加一致性:
extension Int: MemoryLayouted { }
extension String: MemoryLayouted { }
extension MyAwesomeType: MemoryLayouted { }
考虑到上述情况,您可以将初始代码重写为如下内容:
let regularInt: Int = 1
let anyInt: MemoryLayouted = 2
print(regularInt.memoryLayoutSize) // 8
print(anyInt.memoryLayoutSize) // 8
您将获得一致的行为和类型安全,这种类型安全可能会转化为更稳定的应用程序。
PS 一种允许您使用Any
的 hacky 方法可能通过直接 memory 访问解压现有容器来实现。 Swift ABI 在这一点上是稳定的,所以现有的容器布局保证在未来不会改变,但是除非绝对必要,否则不建议走这条路。
也许遇到这个问题并且有 ABI 布局代码经验的人可以提供它的代码。
我要做的是将 Any 转换为所有支持的类型。 当您知道 Int 是什么类型时,为什么还要将其转换为Any
? 反正?
var value: Any = Int(2) as Any
switch value {
case value is Int:
// ... other cases here
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.