【问题标题】:For-loop optimization in Objective-CObjective-C 中的 For 循环优化
【发布时间】:2014-09-28 18:20:44
【问题描述】:

只是寻找一些关于如何最好地优化 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) 自动释放池

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) 别的?

【问题讨论】:

  • 优化什么?时间、美学、可读性、大小、内存使用,其他?
  • 内存使用和/或时间。在我看来,这三个都是可读和美观的。当然也欢迎其他建议。
  • 如果您要进行运行时,我建议您使用 dispatch_benchmark 进行分析并查看您得到的结果。 (nshipster.com/benchmarking)
  • 运行时间不是一个大问题,因为在我的上下文中这是在后台,不需要快速周转,在这种情况下内存更重要。
  • 分配工具告诉您什么?您对高水位标记还是脏内存更感兴趣?

标签: objective-c optimization automatic-ref-counting


【解决方案1】:

firstsecondthird 在所有情况下都能在循环之外存活,因为它们在 dict 中。 @[first, second, third] 的数组仍然存在,因为它已被添加到数组中。

因此,三者之间的差异将非常有限。 valueForKeyPath: 不会生成副本,所以您所说的只是(i)保留计数表中的存储*; (ii) 存储在自动释放池中。

(i) 不仅可以忽略不计,而且对于当前的运行时实现是如此的内部,以至于没有必要依赖它。 (ii) 也可以忽略不计,但规范明确要求。

从技术上讲,@autoreleasepool 可能会稍微紧凑一些(取决于空池的总大小与向池中添加对象所需的大小与数组的长度),但我不会沉迷于它.

前两个选项之间绝对没有区别。存储的本地化程度不会影响 ARC — 即使不依赖确切的 ARC 实现细节,无论如何,您都会在下一次迭代的顶部重新分配。

(* 保留计数在大于 1 之前不会存储,因为 1 是一个非常常见的值,并且由根本存在的对象隐含;因此它们放在单独的表中,而不是与对象一起)

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

【讨论】:

  • 非常感谢,从保留计数的角度考虑它为我钉了它。
【解决方案2】:

到目前为止最大的速度改进是删除了对 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]]];
}

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

【讨论】:

    猜你喜欢
    • 2018-12-23
    • 1970-01-01
    • 2017-09-01
    • 2017-06-08
    • 2020-08-28
    • 1970-01-01
    • 1970-01-01
    • 2011-07-04
    • 2010-11-15
    相关资源
    最近更新 更多