Objective-C中的For循环优化

3

我只是希望得到一些简单建议,关于如何最佳地优化Obj-C和ARC中的for循环(就内存使用而言),以这个未经优化的任意代码为例:

NSMutableArray *array = [NSMutableArray array];

for (NSDictionary *dict in NSArray *arrayOfDicts) {

    NSString *first = [dict valueForKeyPath:@"object.first"];
    NSString *second = [dict valueForKeyPath:@"object.second"];
    NSString *third = [dict valueForKeyPath:@"object.third"];

    [array addObject:@[first, second, third]];

}

哪种方法在实践中更好/正确(假设有很多循环,因此可能很重要)?
1)只声明一次,然后复制到数组中。
NSMutableArray *array = [NSMutableArray array];
NSString *first, *second, *third;

for (NSDictionary *dict in NSArray *arrayOfDicts) {

    first = [dict valueForKeyPath:@"object.first"];
    second = [dict valueForKeyPath:@"object.second"];
    third = [dict valueForKeyPath:@"object.third"];

    [array addObject:@[[first copy], [second copy], [third copy]]];
    first, second, third = nil;

}

2) Autoreleasepool

NSMutableArray *array = [NSMutableArray array];

for (NSDictionary *dict in NSArray *arrayOfDicts) {

    @autoreleasepool {
        NSString *first = [dict valueForKeyPath:@"object.first"];
        NSString *second = [dict valueForKeyPath:@"object.second"];
        NSString *third = [dict valueForKeyPath:@"object.third"];

        [array addObject:@[first, second, third]];
    }

}

3) Something else?


1
优化什么?时间、美观、可读性、大小、内存使用或其他? - zaph
内存使用和/或时间。在我看来,这三个都是可读性和美观性的。当然,也欢迎其他建议。 - amcc
如果你要进行运行时优化,我建议使用dispatch_benchmark进行性能分析,看看你能得到什么结果。(http://nshipster.com/benchmarking/) - Aidan Medcalf
运行时间不是一个很大的问题,因为在我的情况下,这是在后台进行的,没有快速反应的要求,因此内存更重要。 - amcc
分配工具告诉你什么?你更关心高水位标记还是脏内存? - quellish
请使用@autoreleasepool - Bryan Chen
2个回答

3
firstsecondthird无论如何都在字典中,因此在所有情况下都会保留循环之外。@[first, second, third]数组的生存也会在所有情况下保留,因为已经添加到了数组中。

因此,在这三种情况下几乎没有区别。valueForKeyPath: 不生成副本,所以你所说的只是(i)在保留计数表内部的存储;以及(ii)在自动释放池中的存储。

(i)不仅可以忽略不计,而且在当前运行时的实现中是如此内部化的,以至于依赖它没有任何意义。(ii)也可以忽略不计,但显式地需要符合规范。

从技术上讲,@autoreleasepool可能会更加紧凑(具体取决于空池的总大小与将对象添加到池中所需的大小以及数组的长度),但我不会过度关注这个问题。

前两个选项之间绝对没有区别。存储的本地化并不影响 ARC — 即使不依赖精确的 ARC 实现细节,在下一次迭代的顶部重新分配也是一样的。

(* 保留计数只有在大于1时才会存储,因为1是非常常见的值,并且由对象的存在隐含;因此它们进入一个单独的表而不是与对象一起)

2018年编辑:64位运行时实际上将大于1但小于一个非常大的数字的保留计数存储在isa指针的一部分中,因为当前不需要整个64位范围。如果需要,他们可以将保留计数移出来。原始的64位运行时中,这个非常大的数字是2^19 + 1,但它的大小是不透明的,所以可能已经改变。因此,在现代设备上,附加的保留基本不会增加内存占用。


非常感谢,从保留计数的角度考虑让我恍然大悟。 - amcc

1
到目前为止最大的速度提升将是删除对valueForKeyPath的免费调用。
valueForKeyPath接受一个字符串参数,例如object.first。然后必须分析该字符串:找到中间的点,确定接收者是字典,"object"不以@字符开头,因此对应于objectForKey等等。所有这些需要做三次。相反,
for (NSDictionary *dict in NSArray *arrayOfDicts) {
    SomeObject* someObject = [dict objectForKey:@"object"];

    [array addObject:@[[someObject.first copy], 
                       [someObject.second copy],
                       [someObject.third copy]]];
}

由您决定是否需要复制。对象通常具有“复制”属性,因此第一、第二、第三个可能已经是副本。


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