NSString - статический или встроенный? Есть ли выигрыш в производительности?

Есть ли увеличение производительности, если я пишу

- (NSString *)helloStringWithName:(NSString *)name
    static NSString *formatString = @"Hello %@!";
    return [NSString stringWithFormat:formatString, name];
}

вместо

- (NSString *)helloStringWithName:(NSString *)name
    return [NSString stringWithFormat:@"Hello %@!", name];
}

??

Если бы я предполагал, что я думаю, что последний создается и автореализован каждый раз, когда выполняется код, но я думаю, что компилятор достаточно умен, чтобы знать, что здесь делать.

Ответ 1

Строковые литералы в Objective-C выделяются во время компиляции, поэтому вы не сможете получить что-либо в производительности.

Рассматривает это

NSString * str = @"Hello";    
NSString * str2 = @"Hello";

NSLog(@"%p", str);  // => 0x860358
NSLog(@"%p", str2); // => 0x860358

Поэтому, если вы намерены сказать что-то вроде:

"Привет, компилятор, строка формата всегда одна и та же, поэтому не беспокойтесь о том, чтобы выделить это более одного раза"

ответ будет следующим:

"Как хорошо, скажи мне, что я не знаю"

Примечание

Если вы все еще настроены скептически, вы можете взглянуть на сборку (проверьте ответ Дирка), но позвольте мне дать вам несколько советов: не ударяйте головой о таких проблемах с производительностью.

Накладные расходы на использование NSString и абстракция высокого уровня в качестве объектов в целом определенно преобладают в ваших программах, поэтому, даже если вы наберете наносекунду на строку, вы не сможете это даже заметить. И, как вы по праву подозреваете, компилятор уже достаточно умен, чтобы заботиться о таких деталях.

Завершить это: пусть компилятор выполнит свою работу, и вы сделаете свое, то есть написали читаемый и поддерживаемый код.

Ответ 2

Если вы попробуете это (Menu- > Product- > Generate Output- > Assembly File), вы увидите практически идентичный вывод под clang - с небольшими изменениями из-за дополнительной информации о параметрах и информации об отставке.

Итак, короче - никакой реальной разницы; хотя статический, возможно, немного легче отлаживать, я думаю.

    .align  4, 0x90
"-[Foo helloStringWithName1:]":         ## @"\01-[Foo helloStringWithName1:]"
    .cfi_startproc
Lfunc_begin0:
    .loc    1 15 0                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:15:0
## BB#0:
    pushq   %rbp
Ltmp2:
    .cfi_def_cfa_offset 16
Ltmp3:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp4:
    .cfi_def_cfa_register %rbp
    subq    $48, %rsp
    movq    %rdi, -8(%rbp)
    movq    %rsi, -16(%rbp)
    movq    %rdx, %rdi
    callq   _objc_retain
    movq    %rax, -24(%rbp)
    .loc    1 17 5 prologue_end     ## /Users/dirkx/tmp/ccccc/ccccc/main.m:17:5
Ltmp5:
    movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rax
    movq    "-[Foo helloStringWithName1:].formatString"(%rip), %rdx
    movq    -24(%rbp), %rcx
    movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi
    movq    %rax, %rdi
    movb    $0, %al
    callq   _objc_msgSend
    movq    %rax, %rdi
    callq   _objc_retainAutoreleasedReturnValue
    movabsq $0, %rsi
    leaq    -24(%rbp), %rcx
    movl    $1, -28(%rbp)
    .loc    1 18 1                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:18:1
    movq    %rcx, %rdi
    movq    %rax, -40(%rbp)         ## 8-byte Spill
    callq   _objc_storeStrong
Ltmp6:
    .loc    1 18 1                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:18:1
    movq    -40(%rbp), %rax         ## 8-byte Reload
    movq    %rax, %rdi
    callq   _objc_autoreleaseReturnValue
    .loc    1 17 5                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:17:5
Ltmp7:
    addq    $48, %rsp
    popq    %rbp
    ret
Ltmp8:
Lfunc_end0:
    .cfi_endproc

против

    .align  4, 0x90
"-[Foo helloStringWithName2:]":         ## @"\01-[Foo helloStringWithName2:]"
    .cfi_startproc
Lfunc_begin1:
    .loc    1 20 0                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:20:0
## BB#0:
    pushq   %rbp
Ltmp11:
    .cfi_def_cfa_offset 16
Ltmp12:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp13:
    .cfi_def_cfa_register %rbp
    subq    $48, %rsp
    movq    %rdi, -8(%rbp)
    movq    %rsi, -16(%rbp)
    movq    %rdx, %rdi
    callq   _objc_retain
    leaq    L__unnamed_cfstring_2(%rip), %rdx  <-------------------- 
    movq    %rax, -24(%rbp)
    .loc    1 21 5 prologue_end     ## /Users/dirkx/tmp/ccccc/ccccc/main.m:21:5
Ltmp14:
    movq    L_OBJC_CLASSLIST_REFERENCES_$_(%rip), %rax
    movq    -24(%rbp), %rcx
    movq    L_OBJC_SELECTOR_REFERENCES_(%rip), %rsi
    movq    %rax, %rdi
    movb    $0, %al
    callq   _objc_msgSend
    movq    %rax, %rdi
    callq   _objc_retainAutoreleasedReturnValue
    movabsq $0, %rsi
    leaq    -24(%rbp), %rcx
    movl    $1, -28(%rbp)
    .loc    1 22 1                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:22:1
    movq    %rcx, %rdi
    movq    %rax, -40(%rbp)         ## 8-byte Spill
    callq   _objc_storeStrong
Ltmp15:
    .loc    1 22 1                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:22:1
    movq    -40(%rbp), %rax         ## 8-byte Reload
    movq    %rax, %rdi
    callq   _objc_autoreleaseReturnValue
    .loc    1 21 5                  ## /Users/dirkx/tmp/ccccc/ccccc/main.m:21:5
Ltmp16:
    addq    $48, %rsp
    popq    %rbp
    ret
Ltmp17:
Lfunc_end1:
    .cfi_endproc

с ключевыми ссылками:

Lfunc_end2:
    .cfi_endproc

    .section    __DATA,__data
    .align  3                       ## @"\01-[Foo helloStringWithName1:].formatString"
"-[Foo helloStringWithName1:].formatString":
    .quad   L__unnamed_cfstring_

    .section    __TEXT,__cstring,cstring_literals
l_.str:                                 ## @.str
    .asciz   "Hello 1 %@!"

и

    .section    __TEXT,__cstring,cstring_literals
l_.str1:                                ## @.str1
    .asciz   "Hello 2 %@!"