何时使用dispatch_once与重新分配? (可可/ CocoaTouch)

时间:2016-03-25 11:49:10

标签: objective-c cocoa cocoa-touch

我经常使用简单的非编译时不可变对象:如数组@[@"a", @"b"]或字典@{@"a": @"b"}

我一直在重新分配它们之间的斗争:

- (void)doSomeStuff {
    NSArray<NSString *> *fileTypes = @[@"h", @"m"];
    // use fileTypes
}

并分配一次:

- (void)doSomeStuff {
    static NSArray<NSString *> * fileTypes;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        fileTypes = @[@"h", @"m"];
    });
    // use fileTypes
}

是否有关于何时使用每个构造的建议?像:

  • 取决于分配对象的大小
  • 取决于分配的频率
  • 取决于设备(iPhone 4 vs iMac 2016)
  • ...

我怎么弄清楚?

4 个答案:

答案 0 :(得分:2)

你的子弹列表是一个好的开始。记忆将是另一个考虑因素。从实际初始化到应用程序终止之前,static变量将保留在内存中。显然,在方法结束时将释放一个局部变量(假设没有进一步的引用)。

可读性也值得考虑。重新分配行比dispatch_once设置更容易阅读。

对于一个小阵列,我将重新分配作为首选。开销很小。除非您在紧密循环中创建阵列,否则性能可以忽略不计。

我会将dispatch_oncestatic变量用于需要更多开销的事情,例如创建日期格式化程序。但是,对用户做出反应来改变设备的语言环境会产生开销。

最后,我的思考过程是首先使用重新分配。然后我考虑使用staticdispatch_once是否有实际好处。如果没有值得使用static的理由,我会留下一个局部变量。

如果重新分配的开销(速度)过高(如果永久性内存命中太大,则不使用{}},请使用static

答案 1 :(得分:1)

你的第二种方法更复杂。所以你应该使用它,如果需要的话,但不是默认的。

通常,当对象创建极其昂贵(几乎从不)或者您需要实例对象的单个标识(共享实例,有时称为单例,不正确)时,这样做。在这种情况下,您将认识到你需要它。

答案 2 :(得分:1)

虽然这个问题可能会被关闭为主要基于意见的问题,但我认为这个问题解决了程序员经常做出的选择。

显而易见的答案是:不要优化,如果你这样做,请先进行配置 - 因为大部分时间你会直观地解决代码的错误部分。

话虽如此,我在这里解决这个问题。

  • 如果构建所需的对象(如每个文档的格式化程序)和资源上的轻量级,则重用/缓存对象。
  • 如果对象构建起来很便宜,请在需要时创建一个新实例。
  • 对于在主线程(UI东西)上运行的关键事物,我倾向于更严格地绘制线条并提前缓存。

根据您对这些对象的需求,有时会有更便宜的有效替代方案,但提供类似的编程舒适度。例如,如果你想查找一些棋盘坐标,你可以建立一个字典{@&#34; a1&#34; :0,@&#34; b1&#34; :1,...},单独使用一个数组和索引,取一个普通的C数组(现在附加了一个更低的价格标签),或者做一个小的基于整数的计算。

要考虑的另一点是放置缓存对象的位置。例如,您可以在方法中静态存储它,如示例中所示。或者你可以把它变成一个实例变量。或类属性/静态变量。有时缓存只是目标的一半 - 如果您考虑使用包含日期格式化程序的单元格的表格视图,您可以考虑为所有单元格重用相同的格式化程序。有时您可以将重用的对象重构为辅助对象(无论是否为单例),并解决其中的一些问题。

所以这里确实没有金色的子弹,每种情况都需要一个单独的方法。只是不要陷入过早优化的陷阱,并交换明确的错误代码,几乎不可读的东西可能对你的性能无关,但带来了确定的缺点,如增加内存占用。

答案 3 :(得分:0)

<强> dispatch_once_t

这有两个主要好处:1)保证在应用程序运行的生命周期内只调用一次方法,2)它可以用于实现延迟初始化,如报告的那样在下面的男人页面中。

来自dispatch_once()的{​​{3}}:

  

dispatch_once()函数提供了一种简单有效的机制来运行初始化器一次,类似于pthread_once(3)。设计良好的代码隐藏了延迟初始化的使用。

dispatch_once_t的一些用例

  1. 的Singleton
  2. 文件系统资源的初始化,例如文件句柄
  3. 将由一组实例共享并占用大量内存的任何静态变量
  4. 静态,没有dispatch_once_t

    未包含在dispatch_once块中的静态声明变量仍然具有被许多实例共享的好处。例如,如果您有一个名为defaultColor的静态变量,则该对象的所有实例都会看到相同的值。因此,它是特定于类的,而不是特定于实例的。

    但是,每当您需要保证只会调用一次块时,您需要使用dispatch_once_t

    <强>不变性

    您还提到了不变性。不可变性独立于运行一次且仅运行一次的关注 - 因此存在静态不可变变量和实例不可变变量的情况。例如,有时您可能需要初始化不可变对象,但每个实例仍然可能不同(在其值取决于其他实例变量的情况下)。在这种情况下,不可变对象不是静态的,并且仍然可以使用来自多个实例的不同值进行初始化。在这种情况下,属性是从其他实例变量派生的,因此不应允许在外部进行更改。

    关于不变性与可变性的说明,来自OS X Man Page

      

    考虑所有对象都能够变异的场景。在您的应用程序中,您调用一个方法并将对表示字符串的对象的引用传回。您可以在用户界面中使用此字符串来标识特定的数据。现在,应用程序中的另一个子系统会自己引用相同的字符串,并决定对其进行变异。突然间,你的标签已经从你身下变了。例如,如果您获得对用于填充表视图的数组的引用,则事情会变得更加可怕。用户选择与阵列中的对象相对应的行,该对象已经被程序中的其他地方的某些代码移除,并且随后出现问题。不变性是一种保证,当你使用它时,一个物体的价值不会意外地改变。

         

    适合不变性的对象是封装离散值集合或包含存储在缓冲区中的值的对象(缓冲区本身就是各种集合,可以是字符或字节)。但并非所有这些价值对象都必然会受益于可变版本。包含单个简单值的对象(例如NSNumber或NSDate的实例)不是可变性的良好候选者。当在这些情况下表示的值发生变化时,用新实例替换旧实例会更有意义。

    关于效果的说明,来自Concepts in Objective-C Programming

      

    性能也是表示字符串和字典之类的对象的不可变版本的原因。基本实体(如字符串和字典)的可变对象会带来一些开销。因为它们必须动态地管理可更改的后备存储 - 根据需要分配和释放内存块 - 可变对象的效率可能低于它们的不可变对象。