Почему использование isEmpty предпочтительнее, чем сравнение с пустым строковым литералом Swift?
Тип String - Swift имеет свойство с именем isEmpty
это указывает, нет ли в строке символов.
Я хотел бы знать, есть ли разница между использованием isEmpty
и проверка равенства пустому строковому литералу. Другими словами, этоmyString.isEmpty
лучше чем myString == ""
?
Я провел небольшое исследование и нашел следующие рекомендации:
- Ссылка на строковую структуру в документации для разработчиков Apple (а также в Swift Language Guide) рекомендует использовать
isEmpty
вместо проверки длины строки:
Чтобы проверить, пуста ли строка, используйте ее
isEmpty
свойство вместо сравнения длины одного из представлений с0
. В отличие от isEmpty, вычисление свойства count для представления требует перебора элементов строки.
В ответе Роба Нэпьера на несколько иной вопрос 2015 года на Stackru говорится следующее:
Пустая строка - единственная пустая строка, поэтому не должно быть случаев, когда
string.isEmpty()
не возвращает то же значение, что иstring == ""
. Конечно, они могут делать это в разное время и в разной памяти. Используют ли они разное количество времени и памяти - это не описанные детали реализации, ноisEmpty
является предпочтительным способом проверки в Swift (как указано в документации).Некоторые сообщения в блогах также рекомендуют использовать
isEmpty
особенно вместо того, чтобы проверять, равна ли длина строки 0.
Однако ни один из этих источников не говорит ничего против сравнения с пустым литералом.
Совершенно разумно избегать таких конструкций, как myString.count == 0
из-за очевидных недостатков производительности и удобочитаемости. Я также понимаю, чтоmyString.isEmpty
более читабелен, чем myString == ""
.
Тем не менее, мне любопытно, плохо ли сравнение с пустым строковым литералом. Действительно ли это влияет на память или производительность? Возможно, компилятор Swift в наши дни настолько умен, что будет производить тот же двоичный код дляmyString.isEmpty
а также myString == ""
? Я чувствую, что разница должна быть незначительной или даже отсутствовать, но у меня нет доказательств.
Я понимаю, что это не совсем практический вопрос, однако я был бы признателен, если бы кто-нибудь мог поделиться некоторыми мыслями о том, как эти две альтернативы работают на более низком уровне, и есть ли какие-либо различия. Спасибо всем заранее.
1 ответ
В качестве примечания,
isEmpty
является предпочтительным / рекомендуемым методом проверки пустоты коллекции, так как все
Collection
типы гарантируют, что
isEmpty
возвращается в O(1) (или, по крайней мере, это справедливо для коллекций стандартной библиотеки). Оператор равенства не дает таких гарантий, поэтому, если вас интересует только коллекция, имеющая или не имеющая элементов (например, для запуска операции обработки), тогда
isEmpty
это определенно путь.
Теперь, чтобы увидеть, что происходит под капотом при использовании
isEmpty
vs при сравнении с пустой строкой мы можем использовать сгенерированную сборку.
func testEmpty(_ str: String) -> Bool { str.isEmpty }
приводит к следующему ассемблерному коду:
_$s3CL29testEmptyySbSSF:
0000000100002c70 push rbp
0000000100002c71 mov rbp, rsp
0000000100002c74 mov rax, rsi
0000000100002c77 shr rax, 0x38
0000000100002c7b and eax, 0xf
0000000100002c7e movabs rcx, 0xffffffffffff
0000000100002c88 and rcx, rdi
0000000100002c8b bt rsi, 0x3d
0000000100002c90 cmovae rax, rcx
0000000100002c94 test rax, rax
0000000100002c97 sete al
0000000100002c9a pop rbp
0000000100002c9b ret
в то время как
func testEqual(_ str: String) -> Bool { str == "" }
генерирует это:
_$s3CL29testEqualySbSSF:
0000000100002cd0 push rbp
0000000100002cd1 mov rbp, rsp
0000000100002cd4 movabs rcx, 0xe000000000000000
0000000100002cde test rdi, rdi
0000000100002ce1 jne 0x100002cec
0000000100002ce3 cmp rsi, rcx
0000000100002ce6 jne 0x100002cec
0000000100002ce8 mov al, 0x1
0000000100002cea pop rbp
0000000100002ceb ret
0000000100002cec xor edx, edx ; XREF=_$s3CL29testEqualySbSSF+17, _$s3CL29testEqualySbSSF+22
0000000100002cee xor r8d, r8d
0000000100002cf1 pop rbp
0000000100002cf2 jmp imp___stubs__$ss27_stringCompareWithSmolCheck__9expectingSbs11_StringGutsV_ADs01_G16ComparisonResultOtF
; endp
Обе сборки создаются в режиме Release со всеми включенными оптимизациями. Кажется, что для
isEmpty
вызов компилятора может использовать некоторые ярлыки, поскольку он знает о внутреннем
String
структура.
Но мы можем убрать это, сделав наши функции универсальными:
func testEmpty<S: StringProtocol>(_ str: S) -> Bool { str.isEmpty }
производит
_$s3CL29testEmptyySbxSyRzlF:
0000000100002bd0 push rbp
0000000100002bd1 mov rbp, rsp
0000000100002bd4 push r13
0000000100002bd6 push rax
0000000100002bd7 mov rax, rsi
0000000100002bda mov rcx, qword [ds:rdx+8]
0000000100002bde mov rsi, qword [ds:rcx+8]
0000000100002be2 mov r13, rdi
0000000100002be5 mov rdi, rax
0000000100002be8 call imp___stubs__$sSl7isEmptySbvgTj
0000000100002bed add rsp, 0x8
0000000100002bf1 pop r13
0000000100002bf3 pop rbp
0000000100002bf4 ret
; endp
, в то время как
func testEqual<S: StringProtocol>(_ str: S) -> Bool { str == "" }
производит
_$s3CL29testEqualySbxSyRzlF:
0000000100002c00 push rbp
0000000100002c01 mov rbp, rsp
0000000100002c04 push r14
0000000100002c06 push r13
0000000100002c08 push rbx
0000000100002c09 sub rsp, 0x18
0000000100002c0d mov r14, rdx
0000000100002c10 mov r13, rsi
0000000100002c13 mov rbx, rdi
0000000100002c16 mov qword [ss:rbp+var_28], 0x0
0000000100002c1e movabs rax, 0xe000000000000000
0000000100002c28 mov qword [ss:rbp+var_20], rax
0000000100002c2c call _$sS2SSysWl
0000000100002c31 mov rcx, qword [ds:imp___got__$sSSN]
0000000100002c38 lea rsi, qword [ss:rbp+var_28]
0000000100002c3c mov rdi, rbx
0000000100002c3f mov rdx, r13
0000000100002c42 mov r8, r14
0000000100002c45 mov r9, rax
0000000100002c48 call imp___stubs__$sSysE2eeoiySbx_qd__tSyRd__lFZ
0000000100002c4d add rsp, 0x18
0000000100002c51 pop rbx
0000000100002c52 pop r13
0000000100002c54 pop r14
0000000100002c56 pop rbp
0000000100002c57 ret
; endp
Аналогичные результаты,
isEmpty
код результатов меньше кода сборки, что делает его быстрее.
Никакой разницы в производительности (по сравнению с == ""). Так просто удобнее.