使用ARC,有什么更好的:alloc或autorelease初始化器?

ma1*_*w28 41 cocoa cocoa-touch memory-management objective-c automatic-ref-counting

使用allocautorelease初始化器更好(更快,更高效).例如:

- (NSString *)hello:(NSString *)name {
    return [[NSString alloc] initWithFormat:@"Hello, %@", name];
}
Run Code Online (Sandbox Code Playgroud)

要么

- (NSString *)hello:(NSString *)name {
    return [NSString stringWithFormat:@"Hello, %@", name];
//    return [@"Hello, " stringByAppendingString:name]; // even simpler
}
Run Code Online (Sandbox Code Playgroud)

我知道在大多数情况下,这里的表现无关紧要.但是,我仍然希望养成以更好的方式做这件事的习惯.

如果他们做同样的事情,那么我更喜欢后一种选择,因为它的输入更短,更具可读性.

在Xcode 4.2,有没有办法,看看有什么ARC编译成,也就是说,它把retain,release,autorelease,等?切换到ARC时,此功能非常有用.我知道你不应该考虑这些东西,但它能帮助我弄清楚这些问题的答案.

Stu*_*nie 37

差异很微妙,但您应该选择autorelease版本.首先,您的代码更具可读性.其次,在检查优化的装配输出时,autorelease版本稍微更优化.

autorelease版本中,

- (NSString *)hello:(NSString *)name {
    return [NSString stringWithFormat:@"Hello, %@", name];
}
Run Code Online (Sandbox Code Playgroud)

翻译成

"-[SGCAppDelegate hello:]":
    push    {r7, lr}
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4))
    mov r3, r2
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_-(LPC0_0+4))
    movw    r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4))
    movt    r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC0_1+4))
    add r1, pc
    add r0, pc
    mov r7, sp
    ldr r1, [r1]
    ldr r0, [r0]
    movw    r2, :lower16:(L__unnamed_cfstring_-(LPC0_2+4))
    movt    r2, :upper16:(L__unnamed_cfstring_-(LPC0_2+4))
    add r2, pc
    blx _objc_msgSend    ; stringWithFormat:
    pop {r7, pc}
Run Code Online (Sandbox Code Playgroud)

而[[alloc] init]版本如下所示:

"-[SGCAppDelegate hello:]":
    push    {r4, r5, r6, r7, lr}
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    add r7, sp, #12
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    movw    r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    movt    r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    add r1, pc
    add r0, pc
    ldr r5, [r1]
    ldr r6, [r0]
    mov r0, r2
    blx _objc_retain    ; ARC retains the name string temporarily
    mov r1, r5
    mov r4, r0
    mov r0, r6
    blx _objc_msgSend   ; call to alloc
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    mov r3, r4
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    add r1, pc
    ldr r1, [r1]
    movw    r2, :lower16:(L__unnamed_cfstring_-(LPC1_3+4))
    movt    r2, :upper16:(L__unnamed_cfstring_-(LPC1_3+4))
    add r2, pc
    blx _objc_msgSend   ; call to initWithFormat:
    mov r5, r0
    mov r0, r4
    blx _objc_release   ; ARC releases the name string
    mov r0, r5
    pop.w   {r4, r5, r6, r7, lr}
    b.w _objc_autorelease
Run Code Online (Sandbox Code Playgroud)

正如预期的那样,它会更长一点,因为它正在调用allocinitWithFormat:方法.特别有趣的是ARC在这里生成次优代码,因为它保留了name字符串(通过调用_objc_retain注释),并在调用之后释放initWithFormat:.

如果我们添加__unsafe_unretained所有权限定符,如下例所示,代码将以最佳方式呈现. __unsafe_unretained指示编译器使用原语(复制指针)赋值语义.

- (NSString *)hello:(__unsafe_unretained NSString *)name {
    return [[NSString alloc] initWithFormat:@"Hello, %@", name];
}
Run Code Online (Sandbox Code Playgroud)

如下:

"-[SGCAppDelegate hello:]":
    push    {r4, r7, lr}
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    add r7, sp, #4
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_2-(LPC1_0+4))
    movw    r0, :lower16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    movt    r0, :upper16:(L_OBJC_CLASSLIST_REFERENCES_$_-(LPC1_1+4))
    add r1, pc
    add r0, pc
    mov r4, r2
    ldr r1, [r1]
    ldr r0, [r0]
    blx _objc_msgSend
    movw    r1, :lower16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    mov r3, r4
    movt    r1, :upper16:(L_OBJC_SELECTOR_REFERENCES_4-(LPC1_2+4))
    add r1, pc
    ldr r1, [r1]
    movw    r2, :lower16:(L__unnamed_cfstring_-(LPC1_3+4))
    movt    r2, :upper16:(L__unnamed_cfstring_-(LPC1_3+4))
    add r2, pc
    blx _objc_msgSend
    .loc    1 31 1
    pop.w   {r4, r7, lr}
    b.w _objc_autorelease
Run Code Online (Sandbox Code Playgroud)

  • 返回一个自动释放的对象(假设ARC编译器没有优化你的特定上下文中的自动释放)意味着该对象将被释放*稍后*,所以这并不意味着在对象创建点比较组件有点实际意义? (6认同)
  • 由于`stringWithFormat:`本身调用`[[self alloc] initWithFormat:...]`,通过直接调用它来避免额外的方法调度会更有效.编译到二进制文件中的代码越少,并不一定意味着更高效. (4认同)
  • @StuartCarnie你是对的,`@ autoreleasepool`比`NSAutoreleasePool`快.我的观点是,不向池添加对象的版本比向池中添加对象的版本要快.我在iOS和Mac上对此进行了测量,并在下面添加了一个答案.`[NSString stringWithFormat:]`将匹配`[[NSString alloc] initWithFormat:]`的性能,如果ARC优化会阻止将对象添加到池中(通过objc_autoreleaseReturnValue和objc_retainAutoreleasedReturnValue),但该优化不起作用这个案子呢. (2认同)

Tam*_*ese 10

[NSString stringWithFormat:]是更少的代码.但请注意,对象可能最终会出现在自动释放池中.即使使用ARC和-Os编译器优化,目前也会发生这种情况.

目前,[[NSString alloc] initWithFormat:]iOS(使用iOS 5.1.1和Xcode 4.3.3测试)和OS X(使用OS X 10.7.4和Xcode 4.3.3测试)的性能更佳.我修改了@Pascal的示例代码以包含自动释放池的排放时间并得到以下结果:

  • ARC优化不会阻止对象在自动释放池中结束.
  • 包括用于清除具有100万个对象的发布池的时间,[[NSString alloc] initWithFormat:]在iPhone 4S上快约14%,在OS X上快约8%
  • 在循环周围有一个@autoreleasepool会释放循环中的所有对象,从而占用大量内存.

    在iOS 5.1上显示[NSString stringWithFormat:]而不是[[NSString alloc] initWithFormat:]的内存峰值的工具

  • 通过在循环内使用@autoreleasepool可以防止内存峰值.性能保持大致相同,但内存消耗则持平.