这个 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]
}
}
是否会在每次访问 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]
}
}