【问题标题】:Why can't an @IBOutlet be assigned let instead of var in Swift?为什么不能在 Swift 中分配 @IBOutlet 而不是 var?
【发布时间】:2017-10-31 20:18:22
【问题描述】:

我正在关注有关 Swift 的教程,我注意到作者在声明 @IBOutlet 变量时使用 var 而不是 let。所以我很好奇为什么我不能使用let,因为即使对象是常量,对象的属性仍然是可变的,或者不是这种情况?

使用let时Xcode显示的错误是

@IBOutlet 属性要求属性是可变的

但我很困惑,因为 questionLabelUILabel 对象,不一定是对象的属性。还是questionLabel 对象是当前viewController 的一个属性?

import UIKit

class ViewController: UIViewController {

    @IBOutlet let questionLabel: UILabel!

}

如果我分析过度了,请提前感谢您。

【问题讨论】:

标签: ios swift xcode


【解决方案1】:

@IBOulet 标记的属性通常是使用界面构建器连接的 ViewController 的属性。您在界面构建器中创建的视图必须在实际应用程序运行时将其中的界面元素实际连接到属性。

出于这个原因,它首先使用 some init 创建一个新的 ViewController,而不连接任何界面元素。他们只会在稍后阶段建立联系。为了使运行时能够在对象创建完成后将属性连接到视图元素,它们不能是常量,它们必须是可变的。因为在初始化器完成后它们没有值,所以它们必须是可选的。并且为了避免之后使用属性变得麻烦,它们是隐式展开的选项,因此您不必编写 label!.propertylabel.property 就足够了。

这就是为什么当您尝试使用无法连接的IBOutlet 变量执行某些操作时您的代码崩溃的原因,这也是您无法在初始化程序中使用/更改/操作这些字段的原因。

关于您实际的 var / let 混淆。是的,可以更改使用 let 引用的对象本身,例如UILabeltext 但实际对象引用无法更改。这意味着如果你不在初始化器中给常量一个特定的值,它将永远保持nil

【讨论】:

    【解决方案2】:

    原因很简单,它不是在初始化期间(在initXXX 方法中)而是稍后在加载视图时分配的。

    编译器实际上甚至无法确定变量是否被赋值,因为视图加载是完全动态的。

    【讨论】:

      【解决方案3】:

      在swift中,所有的vars和let都可以被认为是属性。

      如果属性是用let 声明的,则它是不可变的(常量)。如果使用 var 关键字声明它是可变的(变量)。这就是letvar 之间的定义区别。

      Outlets 必须是可变的,因为它们的值在对象初始化之前不会被设置。 (视图控制器被初始化,它的出口没有被立即加载。)

      【讨论】:

      • 我仍将继续完成本教程,因为我喜欢与每个人以及您自己的对话。在我继续之前,我必须问只是为了学习目的......我是否仍然需要为我的 viewController 创建初始化程序,或者它是否可以通过在属性上使用 IBOutlet 并在方法上使用 IBAction 来使用?
      • 您的视图控制器子类通常不需要自定义初始化程序。由于 IBOutlet 属性被声明为可选项,因此它们可以保留为 nil,直到视图加载。
      【解决方案4】:

      你是对的 questionLabelUILabel 类型的对象,但用作 class ViewController 的属性。这就是为什么你有@IBOutlet attribute requires property to be mutable。如果您使用var,则表示属性是可变的。如果您使用let,则表示该属性是不可变的。

      尝试在没有@IBOutlet的情况下创建 questionLabel,看看发生了什么。也许,你可以把 let 放在前面。

      【讨论】:

        【解决方案5】:

        首先创建 ViewController,然后构建视图树。这意味着当 ViewController 完成时,init 这些视图还不存在。它们将通过解析情节提要或 XIB 的类 XML 数据添加到 viewDidLoad 之前。

        我知道这是 Apple 的默认做事方式,但我总是会这样写:

        @IBOutlet let questionLabel: UILabel?
        

        原因很简单,绝对没有证明这个标签会在运行时真正存在。例如,当在多个屏幕上重用 ViewControllers 时,在设置连接后更改布局等可能不会设置此插座。如果您使用定义为UILabel!questionLabel 并且它将是nil,您的应用程序将崩溃。我认为生产中的应用程序不应该因为这种愚蠢的事情而崩溃。

        为了真正的应用程序安全,唯一确定它存在的方法是在代码中构建您的 UI。但是,Storyboards 的易用性确实很吸引人,我仍然经常使用它们。

        【讨论】:

        • 1.如果您在通过故事板/xib 连接插座之前访问插座,那么您的代码中有一个错误,它应该会崩溃。 2. 如果您通过代码创建标签(或任何 UI 组件),则不应将其标记为插座。即使你在代码中做所有事情,它仍然应该用!而不是?声明,因为我的第一点仍然适用。
        • 有趣,我喜欢这种方法的想法,因为它似乎是一种更安全的做事方式。我可以问一下,当您以编程方式进行操作时,您如何跟踪界面的外观和功能?您使用的是速写本、zeplin、白板,还是您的流程是什么样的?
        • 不得不投反对票,因为这完全是错误的方法。首先,如果您作为程序员做错了什么,生产中的应用程序应该会崩溃。他们应该尽快崩溃:快速失败/尽早失败。将每个出口视为实际的可选项将产生完全不可读和无用的代码。界面元素必须在运行期间出现。恕我直言,使用代码动态生成 UI 更糟糕,但这是有争议的,第一个论点不是。
        • @rmaddy 如果您更喜欢作为开发人员或测试人员看到事情崩溃,那么您很早就知道出了点问题,我当然可以理解。这就是断言错误的用途。但是您的客户不是测试人员,您的 AppStore 零星评论也不是 JIRA。如果您的应用程序在生产中崩溃,因为在某种您没有测试的场景中将线设为粉红色而不是蓝色崩溃,那么您既没有帮助您的客户,也没有帮助您的产品。你也不是万无一失的测试人员。
        • 最近发生在我身上,有人将更改合并到 xib 并且某些网点不再连接。使用? 我不会轻易注意到。
        猜你喜欢
        • 1970-01-01
        • 2018-02-16
        • 2018-01-06
        • 1970-01-01
        • 2018-08-31
        • 2011-06-25
        • 1970-01-01
        • 2016-07-20
        • 1970-01-01
        相关资源
        最近更新 更多