惰性 var 属性初始化两次是否正常?

2024-02-25

当我使用房产时,我遇到过很奇怪的情况lazy关键词。我知道这个关键字表示属性的初始化将被推迟到实际使用变量为止。但是,它并没有像我预期的那样工作。它运行两次。

class TestLazyViewController: UIViewController {

    var name: String = "" {
        didSet {
            NSLog("name self = \(self)")
            testLabel.text = name
        }
    }

    lazy var testLabel: UILabel = {
        NSLog("testLabel self = \(self)")
        let label = UILabel()
        label.text = "hello"
        self.view.addSubview(label)
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        testLabel.setTranslatesAutoresizingMaskIntoConstraints(false)
        NSLayoutConstraint.activateConstraints([NSLayoutConstraint(item: testLabel, attribute: .CenterX, relatedBy: .Equal, toItem: self.view, attribute: .CenterX, multiplier: 1.0, constant: 0.0)])
        NSLayoutConstraint.activateConstraints([NSLayoutConstraint(item: testLabel, attribute: .CenterY, relatedBy: .Equal, toItem: self.view, attribute: .CenterY, multiplier: 1.0, constant: 0.0)])
    }

    @IBAction func testButton(sender: AnyObject) {
        testLabel.text = "world"
    }
}

我写了一个视图控制器进行测试。该视图控制器由另一个视图控制器呈现。然后,name属性设置在prepareForSegue呈现视图控制器的。

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    let vc = segue.destinationViewController as! TestLazyViewController
    println("vc = \(vc)")
    vc.name = "hello"
}

运行测试时,我得到以下结果。

vc = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
2015-05-25 00:26:15.673 testLazy[95577:22267122] name self = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
2015-05-25 00:26:15.673 testLazy[95577:22267122] testLabel self = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>
2015-05-25 00:26:15.674 testLazy[95577:22267122] testLabel self = <testLazy.TestLazyViewController: 0x7fb3d1d16ec0>

如您所见,初始化代码执行了两次。我不知道这是一个错误还是误用的结果。有谁可以让我知道出了什么问题吗?

我猜参考testLabel with self.view初始化代码中不正确。

UPDATE:
我仍然不明白为什么延迟初始化运行两次。这真的是 Swift 的 bug 吗?

最终更新:
@matt 对这个被初始化两次的问题做了很好的解释。我能够获得宝贵的知识lazy关键字有效。谢谢马特。


您的代码的整个概念都是错误的。

  • In prepareForSegue,您不得引用界面目标视图控制器的,因为它没有界面. viewDidLoad还没有运行;视图控制器没有视图,没有插座,什么都没有。

  • 标签属性的惰性初始化程序不应同时将标签添加到界面中。它应该只制作标签并将其返回。

其他需要了解的事情:

  • 引用视图控制器的view在它有视图之前将强制该视图过早加载。错误地执行此操作实际上可能会导致视图加载twice,这可能会产生可怕的后果。

  • 询问视图控制器其视图是否已加载而不强制视图过早加载的唯一方法是isViewLoaded().

您想要执行的操作的正确步骤是:

  • In prepareForSegue,将名称字符串分配给name财产仅此而已。它可以有一个观察者,但该观察者不能引用view如果我们没有view当时,因为这样做会导致view过早加载。

  • In viewDidLoad,只有这样我们才有视图,现在您可以开始填充界面了。viewDidLoad应该创建标签,将其放入界面,然后拿起name属性,并将其分配给标签。


EDIT:

现在,说了这么多......这与你原来的问题有什么关系?你在这里做错的事情如何解释 Swift 正在做的事情,以及 Swift 本身做错了什么?

要查看答案,只需在以下位置放置一个断点:

lazy var testLabel: UILabel = {
    NSLog("testLabel self = \(self)") // breakpoint here
    // ...

您将看到的是,由于您构建代码的方式,我们正在获得以下值testLabel twice 递归地。这是稍微简化的调用堆栈:

prepareForSegue
name.didset
testLabel.getter -> *
viewDidLoad
testLabel.getter -> *

The testLabelgetter 指的是视图控制器的view,这会导致视图控制器的视图被加载,因此它的viewDidLoad被调用并导致testLabelgetter 再次被调用。

请注意,getter 不仅仅是按顺序调用两次。它被调用两次递归地:实际上,它本身就是在调用自己。

Swift 未能抵御的正是这种递归。如果 setter 仅连续调用两次,则惰性初始化器将不会被第二次调用。但就你而言,它是递归的。所以第二次确实如此,惰性初始化器以前从未运行过。它一直started,但从来没有完全的。因此,Swift 现在运行它是合理的——这恰好意味着再次运行它。

所以,从某种意义上说,是的,你已经抓住了斯威夫特的裤子,但为了实现这一点你必须做的事情是如此令人愤慨,以至于可以合理地称之为你自己的错。这可能是 Swift 的错误,但如果是这样,那么它就是一个在现实生活中永远不会遇到的错误。


EDIT:

在 WWDC 2016 关于 Swift 和并发的视频中,Apple 是explicit对这个。在 Swift 1 和 2 中,甚至在 Swift 3 中,lazy实例变量是非原子的,因此初始化程序可以运行twice如果同时从两个上下文调用 - 这正是您的代码所做的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

惰性 var 属性初始化两次是否正常? 的相关文章

  • 如何使用 Javascript 从 Chrome iOS 下载 blob 文件?

    如何使用 Javascript 从 Chrome iOS 下载 blob 文件 我正在从 iOS 下载文件 pdf excel txt png iOS 没有文件系统 这对下载来说是一个问题 我创建了一个代码 根据操作系统和导航器 如果需要
  • 错误 ITMS-90085:“二进制文件中没有体系结构。Lipo 无法检测到捆绑可执行文件中的任何体系结构。”

    操作系统 OS X Yosemite 版本 10 10 1XCode 未安装应用程序加载器3 0 620 电话间隙 3 7 0PhoneGap 构建 在线 build phonegap com 在验证 iTunes 步骤时 出现错误 ITM
  • 将对象映射到 TableView 部分的 Swift 二维数组

    我想不出更好的方法来做到这一点 我将学生对象的所有属性映射到二维数组中 所以我的电视有几个部分 我也不能使用静态表视图 如果是这样 这个问题就不会存在 所以我在 TVC 中的代码 let currentUser PFUser current
  • iOS 中的视频可以进行反卷积吗?

    我想拍摄击球手挥动棒球的镜头 但球棒很模糊 视频为 30 fps 通过研究 我发现反卷积似乎是最小化运动模糊的方法 但我不知道是否或如何在我的 iOS 应用程序后处理中实现它 我希望有人能给我指出正确的方向 比如如何在 iOS 中应用反卷积
  • 如何在 Swift 中解析蓝牙设备发送的浮点数?

    在我的 iOS 应用程序上 我需要解码蓝牙接收到的 Float 值 并从不同的设备 不是 iOS 获取 4 个字节 因此我需要一个 便携式 4 字节 Float 格式 目前发件人正在使用以下格式 数据编码 0xCCBBAAAEE 0xEE
  • 如何获取 HKWorkoutActivityType 的字符串描述?

    我正在开发一项功能 可以从健康应用程序中读取 HKWorkout 样本 我想向用户显示一个描述字符串 告诉他们锻炼是什么 我能看到的唯一可能向用户指示样本代表的活动类型的属性是workoutActivityType财产 我应该如何将 HKW
  • iOS 中通过 USB 进行反向端口转发

    我在桌面上有一个 Web 套接字服务器 在 iPhone 设备上有一个客户端 我想使用 USB 而不是任何网络与他们通信 我已经使用 adb reverse 在 android 上实现了它 但无法找到适用于 iOS 的任何解决方案 我尝试使
  • 当 UIView 通过自动布局调整大小时,会调用什么方法?

    我有一个图像视图 我通过在子类中覆盖以下内容来实现圆角 void setFrame CGRect frame super setFrame frame self layout setCornerRadius frame size width
  • 为什么分割视图控制器必须始终是您创建的任何界面的根?

    在苹果的开发者指南中 他们指出 分割视图控制器必须始终是您创建的任何界面的根 see here http developer apple com library ios featuredarticles ViewControllerPGfo
  • Xcode 8.2 更新后二进制文件无效

    我今天尝试在更新到 Xcode 8 2 后向我的应用程序推送更新 但收到无效的二进制错误 我以前从未见过这个 我的应用程序的 iOS 部署目标是 iOS 9 0 有谁见过这个错误或知道如何修复它 这是电子邮件的内容 解释了二进制文件的无效内
  • 在 iOS 中获取 Facebook 好友时出错

    我正在尝试获取登录用户的 Facebook 好友列表 它在我的帐户中工作正常 但是当我将应用程序详细信息迁移到新帐户并更改应用程序 ID 和应用程序密码时 我收到以下错误 Error Domain com facebook sdk Code
  • iOS图片上的线测量

    我需要一些帮助来开始绘制末端带有圆圈的线条 并测量其长度 我能够划清界限 但无法让它移动 花了几个小时决定在 SO 上发布 因此 请参阅下图并指导我开始 任何使用 Objective C 的示例或教程都会有帮助 谢谢 这个想法看起来实现起来
  • 使用 popToRootViewController 时我丢失了导航栏

    我有一个 iOS 应用程序 其中主屏幕是 UICollectionViewController 从集合视图中选择项目时 视图将推送到该项目的详细信息视图 在细节视图中 我构建了一个从侧面移出的抽屉 滑块 为了让视图看起来像我想要的那样 我隐
  • UITextField 中光标闪烁,但键盘不出现

    我得到了一个带有文本字段的简单详细视图 在详细的viewController中我写了这段代码 void viewDidAppear BOOL animated self textField becomeFirstResponder NSLo
  • 我可以更改导航栏项目的位置吗?

    Here is the snapshot 代码在这里 UIButton leftButton UIButton buttonWithType UIButtonTypeCustom leftButton frame CGRectMake 0
  • 当设备方向改变时以编程方式更新约束的正确方法?

    我在用着storyboard and autolayout 并将 IB 中的约束设置为IBOutlet在相应的视图控制器中 我正在阅读几篇关于如何将纵向和横向的约束更新为不同的帖子 但我仍然不确定应该如何执行此操作 我应该设置新的限制吗 v
  • 允许在 Safari 上聊天应用程序使用 audio.play()

    由于苹果禁用了自动播放音频的功能HTMLMedia Element play https developer mozilla org en US docs Web API HTMLMediaElement play在没有用户交互的 java
  • MapKit 注释未显示在地图上

    我无法让 MKAnnotationViews 显示在 MapKit 的地图上 我正在使用 iOS 7 现在已经在论坛和网络上搜索了很多小时 尝试不同的示例和设置 下面我有 我认为 使其工作的最基本的设置 该应用程序包含一个 ViewCont
  • JavaScript 与 WKWebView 的同步本机通信

    使用 WKWebView 可以在 JavaScript 和 Swift Obj C 本机代码之间进行同步通信吗 这些是我尝试过但失败的方法 方法 1 使用脚本处理程序 WKWebView接收JS消息的新方式是使用委托方法userConten
  • iOS 13 UIActivityViewController 在图像保存后自动呈现以前的 VC

    我正在尝试实现 将图像保存到库 功能 然后返回到当前视图控制器 但在新的 iOS 13 上 它会返回到呈现当前视图控制器的视图控制器 PHPhotoLibrary requestAuthorization status PHAuthoriz

随机推荐