Swift, Why are the memory sizes of 'String' and 'String?' the same?

82 Views Asked by At

Case Int, Int? I realized that in the case of optional variables, a bit is needed to check if nil, so it comes out 1 byte larger.

But why is the memory size of String and String? the same? Not only String, but also Array, Dictionary, and Set produce the same results.

Is it because they support Copy-on-Write? enter image description here

I searched Apple's official documentation, but couldn't find any results, so I'm asking a question.

2

There are 2 best solutions below

1
ytshen On

Since optional is implemented by enum, When the associated value has a tagged pointer,
The extra bit required for the enum is stored in the spare bits of the tagged pointer.
https://forums.swift.org/t/how-do-i-implemented-a-tagged-pointer-in-swift/24729

0
Sweeper On

As you may know, Wrapped?, aka Optional<Wrapped> is an enum:

enum Optional<Wrapped> {
    case some(Wrapped)
    case none
}

This is an enum that only has one case that has an associated value. Its memory layout is described in the "single payload enums" section in the type layout documentation here.

If an enum has a single case with a data type and one or more no-data cases (a "single-payload" enum), then the case with data type is represented using the data type's binary representation, with added zero bits for tag if necessary. If the data type's binary representation has extra inhabitants, that is, bit patterns with the size and alignment of the type but which do not form valid values of that type, they are used to represent the no-data cases, with extra inhabitants in order of ascending numeric value matching no-data cases in declaration order.

In other words, while String is 128 bits in size, not all 128-bit bit patterns represent a valid String. There are some bit patterns that are invalid Strings. The invalid bit-patterns are the "extra inhabitants". Since these are invalid values of String, they can be used to represent the none case (aka nil) of a String?.

The same applies for Array/Set/Dictionary.

As an experiment, try thinking of some 128-bit values in the form of a pair of Ints (i.e. (Int, Int)), then you can try unsafeBitCasting them to String. Some of them will succeed, and some of them will fail.

Another example of a type with extra inhabitants would be Bool. There are only two possible values of Bool, but its size and alignment are both 1 byte. It has 254 extra inhabitants!

On the other hand, Int has no extra inhabitants - no matter how you arrange the bits, they always represent a valid value of Int. So the size of Int? needs to be bigger than an Int to represent nil.