这个 Swift 语法有效吗?

Is this Swift syntax efficient?

是否会在每次访问 getter 时触发 for 循环?或者 Swift 缓存它 'behind the scenes' ?

var colors: [UIColor] {
get {
  var colors = [UIColor]()
  let palette = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), UIColor.orangeColor(), UIColor.purpleColor(), UIColor.yellowColor()]
  var paletteIndex = 0
  for _ in 0..<photos.count {
    colors.append(palette[paletteIndex])
    paletteIndex = paletteIndex == (palette.count - 1) ? 0 : ++paletteIndex
  }
  return colors
}
}

在 objective-c 中,像这样的 getter 将位于对私有 ivar 的检查之后,以便 ivar 设置一次,然后在后续调用中返回 ivar。

您使用的 computed property 实际上并不存储值,并且计算的命名值或计算的 属性 的值未存储在内存中。它在 swift 编程书籍

它会在每次 getter 被调用时触发。这不可能被优化掉。

您可能希望使预设颜色数组与照片保持同步 属性,即在设置照片时直接更改它。

我也不喜欢那个似乎没有必要的紧耦合。我认为重构它可能是值得的,并且只是 return 给定照片索引的特定颜色(使用模运算符很简单)。

所以我的建议是将调色板保留为一个实例变量,并且只需创建您的方法 return palette[index % palette.count],它会立即给出正确的颜色。

我会按照@Eiko 的建议这样做

let palette = [UIColor.redColor(), UIColor.greenColor(), UIColor.blueColor(), UIColor.orangeColor(), UIColor.purpleColor(), UIColor.yellowColor()]

override func viewDidLoad() {
    super.viewDidLoad()
    ...
}

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier("AnnotatedPhotoCell", forIndexPath: indexPath) as UICollectionViewCell
    cell.contentView.backgroundColor = palette[indexPath.item % palette.count]
    return cell
}

您可以使用惰性初始化:

lazy var colors : [UIColor] = {
    var colors = [UIColor.redColor]
    // blablabla
    return colors
}()

当您第一次尝试访问颜色时,这将是 运行 一次。但是,如果颜色需要在您的 class 生命周期内更新,我建议使用一个函数来重新捕获它们:

func recalcColors() -> [UIColor] {
    var colors = [UIColor.redColor]
    // blablabla
    return colors
}

lazy var colors = recalcColors()

当您需要更新它们时,您可以调用 colors = recalcColors()

从Swift逻辑的角度来看,它每次都会运行,但是也就是说,在某些情况下优化器可以将整个计算变成一个常量,在这种情况下它不会没关系。

有两个障碍:palette 数组和 photos.count 属性。理论上,palette 数组永远不会改变,但编译器无法知道这一点,因为它不知道像 redColor() 这样的函数总是 returns 相同的值。因此,将数组创建从 属性.

中提升出来

photos.count 假设 photos 大小在程序运行期间动态变化,可能没有修复。

但这会给你同样的效果,根本不需要创建任何数组:

struct PaletteCycler {
    let palette = [
        UIColor.redColor(), UIColor.greenColor(),
        UIColor.blueColor(), UIColor.orangeColor(),
        UIColor.purpleColor(), UIColor.yellowColor(),
    ]

    subscript(idx: Int)->UIColor {
        return palette[idx%palette.count]
    }
}

let colors = PaletteCycler()

由于一切都是不变的,因此获取颜色的 运行 时间成本非常低。与原始版本不同,这不会每次都创建一个数组。由于 mod 和 fetch 非常高效,并且 palette 变量是常量,因此应该很快。

顺便说一句,如果事情不是那么稳定,而你确实想要一个数组,你可以像这样使用 map 重写你的循环:

let palette = [
    UIColor.redColor(), UIColor.greenColor(),
    UIColor.blueColor(), UIColor.orangeColor(),
    UIColor.purpleColor(), UIColor.yellowColor(),
]

var colors: [UIColor] = {

    // Swift 2.0 syntax. For 1.2, write
    // indices(photos).map
    return photos.indices.map {
        palette[[=11=] % palette.count]
    }

}