20

除了让老派 Objective-C 程序员心脏病发作之外,这样做还有其他性能影响吗:

NSMutableArray *derp = @[].mutableCopy

与此相反:

NSMutableArray *derp = [[NSMutableArray alloc] init];
4

4 回答 4

13

我将给出与以下答案不同的答案。

除非你测量了你的应用程序的性能并发现它缺乏,并且发现它在代码的那部分中缺乏正确,否则你正在针对错误的事情进行优化。(如果你这样做了,你可以很容易地衡量哪个更快。)

您应该优化代码的清晰度、编写速度和正确性的可能性。

在这方面,我更喜欢第二个代码片段而不是第一个。它几乎准确地说明了您要执行的操作。第一个片段不仅更难阅读;它还犯了使用点表示法调用方法的样式错误,而不是获取属性的值。

再加上导致心脏病发作并不好。:)

于 2013-08-27T00:50:46.550 回答
9

类簇没有额外的分配,它与编译器魔法无关。所以是的,您的示例之间存在显着差异(顺便说一句,点符号滥用为-1000 个互联网点。)您的第一个示例有两个分配,第二个只有一个。

由于我们无法访问 NSArray 的实际源代码,我们可以查看 GNUStep(一个开源实现)来了解它们是如何处理它的。在 NSArray.m 中(简化和省略不相关的东西):

static GSPlaceholderArray   *defaultPlaceholderArray;
+ (void) initialize
{
    defaultPlaceholderArray = (GSPlaceholderArray*)
        NSAllocateObject(GSPlaceholderArrayClass, 0, NSDefaultMallocZone());
}

+ (id) alloc
{
    return defaultPlaceholderArray;
}

这里发生的是 NSArray 定义了一个单例占位符对象,它总是在alloc. 当init在这个单例上调用它时,它会实例化正确的私有子类并返回它。

那么,我们如何判断 Apple 的基金会是否也在做同样的事情呢?很简单,我们只运行这个测试:

NSArray *a1 = [NSArray alloc];
NSArray *a2 = [NSArray alloc];
NSLog(@"%p, %p", a1, a2);

> "0x100102f30, 0x100102f30"

a1并且a2确实具有相同的内存位置,这意味着 Apple 也可能使用单例方法。如果我们打印出类名,__NSPlaceholderArray这几乎可以证实它。

所以,是的,坚持[NSMutableArray new]:)

更新: Greg Parker 指出这@[]也是一个单例,因此@[].mutableCopy只导致一次分配。所以在性能方面,这两个例子是相同的。

于 2013-08-27T03:32:47.263 回答
8

很难准确说出创建了多少对象,尤其是在数组字面量的情况下。

官方文档clang@[]扩展为arrayWithObjects:count:,我怀疑它被实现为[[[self alloc] initWithObjects:objects count:count] autorelease].

因此,此时可能会分配第二个对象,因为NSArray它是一个类 cluster,其实现- [NSArray init]可能如下所示:

- (id)init
{
    [self release];
    self = [[__NSArrayI alloc] init];
    return self;
}

为了证实我的猜想,我编写了一个小程序,打印NSArray不同阶段的各种对象的类。请注意,为了真正确定,有必要捕获所有的分配和初始化方法NSArray在我们这样做之前,我们只能推测。但无论如何,这是代码:

#import <Foundation/Foundation.h>


int main()
{
    NSArray *a = [NSArray alloc];
    NSLog(@"NSArray before init: %@", a.class);

    a = [a init];
    NSLog(@"NSArray after init: %@", a.class);

    NSArray *al = @[];
    NSLog(@"Array literal: %@", al.class);

    NSMutableArray *ma1 = [a mutableCopy];
    NSLog(@"NSMutableArray (copied): %@", ma1.class);

    NSMutableArray *ma2 = [NSMutableArray alloc];
    NSLog(@"NSMutableArray (manufactured) before init: %@", ma2.class);

    ma2 = [ma2 init];
    NSLog(@"NSMutableArray (manufactured) after init: %@", ma2.class);

    return 0;
}

这是它打印的内容(NSLog()为清楚起见,已删除杂乱):

h2co3-macbook:~ h2co3$ ./quirk
NSArray before init: __NSPlaceholderArray
NSArray after init: __NSArrayI
Array literal: __NSArrayI
NSMutableArray (copied): __NSArrayM
NSMutableArray (manufactured) before init: __NSPlaceholderArray
NSMutableArray (manufactured) after init: __NSArrayM

编辑:这里有一些涉及挂钩的代码。结果非常有趣,但这会打印很多文本,因此鼓励您编译并运行它。结果是,基本上,初始化程序不直接通过[NSArray init]

#import <Foundation/Foundation.h>
#import <objc/runtime.h>
#import <objc/message.h>

void hook(Class cls, SEL sel, IMP newimp, IMP *old)
{
    Method m = class_getInstanceMethod(cls, sel);
    *old = method_setImplementation(m, newimp);
}

#define CLS(c) objc_getClass(#c)
#define META(c) objc_getMetaClass(#c)

IMP old_$_NSArray_$_alloc;
IMP old_$_NSMutableArray_$_alloc;
IMP old_$_NSPlaceholderArray_$_alloc;
IMP old_$_NSArrayI_$_alloc;
IMP old_$_NSArrayM_$_alloc;

IMP old_$_NSArray_$_init;
IMP old_$_NSMutableArray_$_init;
IMP old_$_NSPlaceholderArray_$_init;
IMP old_$_NSArrayI_$_init;
IMP old_$_NSArrayM_$_init;

id new_$_NSArray_$_alloc(id self, SEL _cmd)
{
    printf("+ [NSArray<%p> alloc]\n", self);
    return old_$_NSArray_$_alloc(self, _cmd);
}

id new_$_NSMutableArray_$_alloc(id self, SEL _cmd)
{
    printf("+ [NSMutableArray<%p> alloc]\n", self);
    return old_$_NSMutableArray_$_alloc(self, _cmd);
}

id new_$_NSPlaceholderArray_$_alloc(id self, SEL _cmd)
{
    printf("+ [NSPlaceholderArray<%p> alloc]\n", self);
    return old_$_NSPlaceholderArray_$_alloc(self, _cmd);
}

id new_$_NSArrayI_$_alloc(id self, SEL _cmd)
{
    printf("+ [NSArrayI<%p> alloc]\n", self);
    return old_$_NSArrayI_$_alloc(self, _cmd);
}

id new_$_NSArrayM_$_alloc(id self, SEL _cmd)
{
    printf("+ [NSArrayM<%p> alloc]\n", self);
    return old_$_NSArrayM_$_alloc(self, _cmd);
}

id new_$_NSArray_$_init(id self, SEL _cmd)
{
    printf("- [NSArray<%p> init]\n", self);
    return old_$_NSArray_$_init(self, _cmd);
}

id new_$_NSMutableArray_$_init(id self, SEL _cmd)
{
    printf("- [NSMutableArray<%p> init]\n", self);
    return old_$_NSMutableArray_$_init(self, _cmd);
}

id new_$_NSPlaceholderArray_$_init(id self, SEL _cmd)
{
    printf("- [NSPlaceholderArray<%p> init]\n", self);
    return old_$_NSPlaceholderArray_$_init(self, _cmd);
}

id new_$_NSArrayI_$_init(id self, SEL _cmd)
{
    printf("- [NSArrayI<%p> init]\n", self);
    return old_$_NSArrayI_$_init(self, _cmd);
}

id new_$_NSArrayM_$_init(id self, SEL _cmd)
{
    printf("- [NSArrayM<%p> init]\n", self);
    return old_$_NSArrayM_$_init(self, _cmd);
}

int main()
{
    hook(META(NSArray), @selector(alloc), (IMP)new_$_NSArray_$_alloc, &old_$_NSArray_$_alloc);
    hook(META(NSMutableArray), @selector(alloc), (IMP)new_$_NSMutableArray_$_alloc, &old_$_NSMutableArray_$_alloc);
    hook(META(__NSPlaceholderArray), @selector(alloc), (IMP)new_$_NSPlaceholderArray_$_alloc, &old_$_NSPlaceholderArray_$_alloc);
    hook(META(__NSArrayI), @selector(alloc), (IMP)new_$_NSArrayI_$_alloc, &old_$_NSArrayI_$_alloc);
    hook(META(__NSArrayM), @selector(alloc), (IMP)new_$_NSArrayM_$_alloc, &old_$_NSArrayM_$_alloc);

    hook(CLS(NSArray), @selector(init), (IMP)new_$_NSArray_$_init, &old_$_NSArray_$_init);
    hook(CLS(NSMutableArray), @selector(init), (IMP)new_$_NSMutableArray_$_init, &old_$_NSMutableArray_$_init);
    hook(CLS(NSPlaceholderArray), @selector(init), (IMP)new_$_NSPlaceholderArray_$_init, &old_$_NSPlaceholderArray_$_init);
    hook(CLS(NSArrayI), @selector(init), (IMP)new_$_NSArrayI_$_init, &old_$_NSArrayI_$_init);
    hook(CLS(NSArrayM), @selector(init), (IMP)new_$_NSArrayM_$_init, &old_$_NSArrayM_$_init);


    NSArray *a = [NSArray alloc];
    NSLog(@"NSArray before init: %@<%p>", a.class, a);

    a = [a init];
    NSLog(@"NSArray after init: %@<%p>", a.class, a);

    NSArray *al = @[];
    NSLog(@"Array literal: %@<%p>", al.class, al);

    NSMutableArray *ma1 = [a mutableCopy];
    NSLog(@"NSMutableArray (copied): %@<%p>", ma1.class, ma1);

    NSMutableArray *ma2 = [NSMutableArray alloc];
    NSLog(@"NSMutableArray (manufactured) before init: %@<%p>", ma2.class, ma2);

    ma2 = [ma2 init];
    NSLog(@"NSMutableArray (manufactured) after init: %@<%p>", ma2.class, ma2);

    return 0;
}
于 2013-08-26T22:15:35.737 回答
4

是的,一个是分配 2 个对象,另一个不是。我愿意[NSMutableArray new]

更新: Greg Parker 是对的,两者都只分配一个对象。

于 2013-08-26T21:57:31.850 回答