Swift字符串:通过字符串字面量与初始化器的区别

12

在其他语言比如Java中,字符串字面值和初始化器获得的字符串其实是有区别的。在Swift中,它们在底层是等效的吗?

例如:


var string:String = ""
var string:String = String()

请参考这篇SO帖子了解Java中文字符与字符串对象之间的区别。


它们都是一样的。 - Leo Dabus
1
String 在 Swift 中是一个结构体,而不像 Java 一样是引用类型。到目前为止,我还没有发现任何情况下它会有所区别。 - Code Different
1个回答

13

据苹果文档,这些声明是等价的:

初始化空字符串

为了创建一个空字符串作为构建更长字符串的起点,可以将一个空字符串字面量赋值给一个变量,或使用初始化语法初始化一个新的String实例:

var emptyString = ""               // empty string literal
var anotherEmptyString = String()  // initializer syntax
// these two strings are both empty, and are equivalent to each other

参考: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/StringsAndCharacters.html

如果我们查看汇编代码,会发现这两个构造函数使用相同的指令。

string.swift:

let str = String()
let str2 = ""

汇编代码(swiftc -emit-assembly string.swift 编译):

    .section    __TEXT,__text,regular,pure_instructions
    .macosx_version_min 14, 3
    .globl  _main
    .align  4, 0x90
_main:
    .cfi_startproc
    pushq   %rbp
Ltmp0:
    .cfi_def_cfa_offset 16
Ltmp1:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp2:
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movq    _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token4@GOTPCREL(%rip), %rax
    movq    _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func4@GOTPCREL(%rip), %rcx
    xorl    %edx, %edx
    movl    %edi, -4(%rbp)
    movq    %rax, %rdi
    movq    %rsi, -16(%rbp)
    movq    %rcx, %rsi
    callq   _swift_once
    movq    _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_token5@GOTPCREL(%rip), %rdi
    movq    _globalinit_33_1BDF70FFC18749BAB495A73B459ED2F0_func5@GOTPCREL(%rip), %rax
    xorl    %r8d, %r8d
    movl    %r8d, %edx
    movq    __TZvOSs7Process5_argcVSs5Int32@GOTPCREL(%rip), %rcx
    movl    -4(%rbp), %r8d
    movl    %r8d, (%rcx)
    movq    %rax, %rsi
    callq   _swift_once
    movq    __TZvOSs7Process11_unsafeArgvGVSs20UnsafeMutablePointerGS0_VSs4Int8__@GOTPCREL(%rip), %rax
    movq    -16(%rbp), %rcx
    movq    %rcx, (%rax)
    callq   __TFSSCfMSSFT_SS
    leaq    L___unnamed_1(%rip), %rdi
    xorl    %r8d, %r8d
    movl    %r8d, %esi
    movl    $1, %r8d
    movq    %rax, __Tv6string3strSS(%rip)
    movq    %rdx, __Tv6string3strSS+8(%rip)
    movq    %rcx, __Tv6string3strSS+16(%rip)
    movl    %r8d, %edx
    callq   __TFSSCfMSSFT21_builtinStringLiteralBp8byteSizeBw7isASCIIBi1__SS
    xorl    %r8d, %r8d
    movq    %rax, __Tv6string4str2SS(%rip)
    movq    %rdx, __Tv6string4str2SS+8(%rip)
    movq    %rcx, __Tv6string4str2SS+16(%rip)
    movl    %r8d, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc

    .globl  __Tv6string3strSS
.zerofill __DATA,__common,__Tv6string3strSS,24,3
    .globl  __Tv6string4str2SS
.zerofill __DATA,__common,__Tv6string4str2SS,24,3
    .section    __TEXT,__cstring,cstring_literals
L___unnamed_1:
    .space  1

    .no_dead_strip  __Tv6string3strSS
    .no_dead_strip  __Tv6string4str2SS
    .linker_option "-lswiftCore"
    .section    __DATA,__objc_imageinfo,regular,no_dead_strip
L_OBJC_IMAGE_INFO:
    .long   0
    .long   512


.subsections_via_symbols
注意,str和str2的声明具有相同的指令:
xorl    %r8d, %r8d
movl    %r8d, %esi
movl    $1, %r8d
movq    %rax, __Tv6string3strSS(%rip)
movq    %rdx, __Tv6string3strSS+8(%rip)
movq    %rcx, __Tv6string3strSS+16(%rip)
movl    %r8d, %edx

# ...

xorl    %r8d, %r8d
movq    %rax, __Tv6string4str2SS(%rip)
movq    %rdx, __Tv6string4str2SS+8(%rip)
movq    %rcx, __Tv6string4str2SS+16(%rip)
movl    %r8d, %eax

您可以通过查看苹果文档来了解有关字符串字面量的更多信息。


1
我现在不在我的构建机器上,但如果你使用-emit-assembly标志编译一个包含这两个声明的Swift文件,它将向你展示这些声明是相同的。 - JAL
@Boon,确实是一样的,这两个表达式不可能有任何不同之处,它们评估为相同的结果,甚至在“引擎盖下”也没有区别,因为标准库的制作方式。如果您能够找到这些表达式之间的差异,那么您就犯了一个错误,因为具有相同评估的两个表达式不能不同。 - Kametrixom
1
@Boon 我知道,这就是为什么我们在谈论Swift的原因。在Swift中(而不是Java),这些表达式评估为相同的结果。此外,JAL刚刚通过查看编译后的汇编代码证明了这一点。 - Kametrixom
1
@Boon 每次你创建像 ""String()String("") 这样的字符串时,重要的不是你创建了多少次,而是里面的内容:所有这些字符串都没有任何内容,因此它们在定义上是相同的。 - Kametrixom
3
即使在Objective-C中,NSString是一个引用类型,在底层上,使用字面量和构造函数经常是相同的。与此有些关联。 - nhgrif
显示剩余6条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接