【问题标题】:Variable 'xxx' was never mutated, consider changing to 'let'变量 'xxx' 从未发生变异,考虑更改为 'let'
【发布时间】:2015-06-10 03:21:32
【问题描述】:

更新到 xcode7-beta 我遇到了一种新的警告。这是我的代码

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var attributes: [UICollectionViewLayoutAttributes]? = super.layoutAttributesForElementsInRect(rect)
    if let layoutInfo = self.layoutInfo {
        attributes?.append(layoutInfo)
    }
    return attributes
}

警告信息是 Variable 'attributes' was never mutated, consider changing to 'let' constant

xcode 为什么说Variable 'attributes' was never mutated

问题更新

当我将代码更改为此时,警告消失了

override func layoutAttributesForElementsInRect(rect: CGRect) -> [UICollectionViewLayoutAttributes]? {
    var attributes: [UICollectionViewLayoutAttributes]? = super.layoutAttributesForElementsInRect(rect)
    if let layoutInfo = self.layoutInfo {
        attributes!.append(layoutInfo)
    }
    return attributes
}

所以强制解包可以把它拿走。但这未必是件好事吧?

【问题讨论】:

  • 您是否尝试更改为让?还能编译吗?
  • 嗨@Icaro 我需要附加我的 layoutInfo 所以我真的需要一个 var
  • 所以一定是编译器的bug!
  • 我倾向于认为在这种特定情况下这可能是一个错误。在 iOS 9 SDK 中,许多 Cocoa Touch 类已经过审核以支持 Objective-C 泛型,编译器可能会将这些新类型的 NSArray 与类型化的 Swift 数组混淆。此处使用 var 提供了使用 let 的建议,但使用 let 会产生错误。
  • 看起来像一个错误。使用attributes!.append(layoutInfo) 而不是attributes?.append(layoutInfo),警告消失:)

标签: swift optional


【解决方案1】:

他们在 WWDC 视频和发行说明中谈到了这一点。

如果您尽可能使用let 而不是var,您将获得更好的性能(更快的速度,更小的空间)。这告诉编译器这个东西是一个常量,而不是一个变量,这个事实允许编译器优化各种东西。

但编译器不能这样做,除非你这样做尽可能使用let。它不会将var 更改为let 你。

因此,在 Swift 2 中,编译器会在构建时进行更智能的分析,并在您使用 var 时发出警告,而您本来可以使用 let。最终此功能将正常工作,此时您应该听取编译器的建议!

【讨论】:

  • " 如果您计划添加更多代码,实际上您将更改此值" - 该代码已经存在。
  • @MartinR 这只是一个测试版,所以显然编译器的智能还不是全部。我并没有说它工作得很好,我只是在描述警告应该是什么! :)
  • 如果是这样,为什么所有编程语言都主要使用变量(而不是常量)?甚至汇编语言也有可变寄存器!
  • 你有关于性能优化的链接吗?它当然更安全,我可以想象一些优化,但无论你修改与否,内存量仍然是一样的。
【解决方案2】:

这也让我发疯了。每当我有一个类并更改成员属性时,它仍然告诉我应该将变量设为常量。例如:

class X { 
    var name = ""
}

var xInstance = X()
xInstance.name = "my name"

据我所知,编译器是正确的。 xInstance 是一个常数。它在初始化期间被分配指向 X 实例的指针,并且从未被分配另一个值。所以 xInstance 是常数。它指向的类不是恒定的。 这在像 C 或 C++ 这样的语言中更为明显,但是当您意识到所有类实例实际上都是指向堆支持类的指针时,这是有道理的。

对于懂 C++ 的人

xInstance 等价于:

X *xInstance

将其变为 let 意味着它更改为:

X * const xInstance

我想大多数人会直观地认为 swift 声明将等同于

X const * xInstance

【讨论】:

  • 这对于像我这样的老化石来说意义重大。 let 给你一个指向变量对象的常量指针。不过,这是一个有趣的问题……如果您声明的是结构而不是类,则整个结构将是不可变的/常量。
  • 格雷格,感谢您的回答! @EliBurke,您会碰巧知道为什么会发生这种情况吗?我对如何合理化这种差异感到困惑?
  • 如果你问我的“有趣的皱纹”评论——结构是值类型(如 Ints 或 Bools)。以与 Int 的字节相同的方式考虑结构的字段。搜索“swift struct vs class”,还有很多关于这个的答案。
【解决方案3】:

通过使用let 声明一个常量,您可以确保它永远不会被更改。这有助于确保您以后不会意外更改它,并且(理论上)可以帮助优化器生成更快的代码。

如果您使用var 声明一个变量,并且您不打算更改它或对其调用变异方法,则使用let 可以帮助您执行该合同。

【讨论】:

    【解决方案4】:

    您已将该对象创建为 var 对象,但该对象的值没有改变,然后更好地让它让它。就是这样。

    根据 Apple 开发人员指南,如果该对象的值将要更改,则创建 var 对象,否则创建 let 变量。最佳实践

    【讨论】:

      【解决方案5】:

      您的代码暗示如果self.layoutInfo 不为零,attributes 可能会发生变异

      也许警告是说没有路径导致self.layoutInfo 不为零,因此属性不需要是 var。

      检查哪些条件可能导致self.layoutInfo 拥有数据

      【讨论】:

        【解决方案6】:

        为什么会出现这个警告?

        因为在 Swift 中“let”的意思是“值是恒定的”并且允许编译器做一些优化,因此“let”比“var”要快,
        从“Swift 2”开始,如果我们使用“var”,编译器就会发出警告,只要它检测到我们可以使用“let”来代替。

        类与结构

        然而,在 Swift 中,“class”是一个引用类型(就像在 Java 中一样),这意味着“值”只是“一个引用和/或指向实际数据的指针”。

        即使我们将 "let" 与 "class" 一起使用,指向的 "data" 也永远不会是恒定的(并且总是可变的)。
        因此,如果更改“数据”是一件大事并且不应该被忽视,请考虑使用struct

        在 Swift 中,“struct”是一种值类型,除非它是用“var”(或“func”参数中的“inout”定义),否则对“数据”的任何突变持有应该会导致编译错误。

        CC++ 语言中的const 关键字基本相同,但在C++ 中,我们不需要从“class”迁移到完全不同的类型,只需要对可变性敏感类。

        这对 C++ 来说是一个优点,但一旦我们看到其他 C++ 过于复杂的情况,它就会消失。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-01-03
          • 1970-01-01
          • 1970-01-01
          • 2018-08-18
          • 1970-01-01
          • 2017-08-17
          相关资源
          最近更新 更多