[英]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.