Swift 类在作用域结束时取消初始化,而不是在上次使用后取消初始化

2023-12-15

我问过这个问题 asking 关于局部变量中引用的生命周期的保证并被提及这个线程其中类实例引用生命周期的确切语义进行了讨论。 AFAICT 从该线程来看,类实例可能会在最后一次使用包含最后一个引用的变量之后立即被取消初始化,甚至在同一语句中的其他表达式被求值之前也是如此。有人认为,允许这种行为对于防止在写时复制数据结构中创建不必要的副本是必要的,我可以遵循这一点。

这引起了我的注意,到目前为止我从未见过 Swift 有这样的行为方式。每当我跟踪执行时,无论是在调试还是发布版本中,类实例仅在离开完整范围时才被初始化。

在下面的例子中main(),我创建了一个类的实例,该实例在其生命周期中打印到控制台。请注意,在调用期间或之后不会使用该实例print() in main():

public final class Something {
    init() { print("Something.init()") }
    deinit { print("Something.deinit") }
}

func main() {
    let something = Something()
    print("in main()")
}

main()

当使用调试或发布配置构建和运行此示例时,我看到以下执行顺序,即实例在调用期间保持活动状态print() in main():

$ swift run -c release
Something.init()
in main()
Something.deinit

相反,我期望以下执行顺序:

$ swift run -c release
Something.init()
Something.deinit
in main()

我正在使用随 Xcode 9.2 一起分发的 Swift 编译器 4.0.3:

$ swift --version
Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
Target: x86_64-apple-macosx10.9

考虑到 Swift 编译器的开发人员正在尝试非常积极地减少 ARC 引用计数器(以及这种反初始化类实例)以避免写入时复制数据结构中不必要的副本,如何解释这个执行顺序?这里是否还有其他一些优化在起作用,因为在这种情况下保持引用活动实际上并不会导致不必要的工作(只是内存分配的时间比严格必要的时间长)?

我的问题不是解决我遇到的特定问题,毕竟编译器正在创建完全符合语言允许的范围的代码。我想很好地了解 Swift 编译器如何处理引用以及哪些优化在起作用,以便我可以衡量哪些代码模式在关键情况下可以带来良好的性能,例如当需要通过写时复制来复制大型数据结构时,或者涉及大量需要递增和递减的引用时。


我们来分析一下 Swift 在内存管理方面的工作原理:

  1. 当不再有对对象的强引用时,该对象将被释放
  2. 当声明变量的作用域结束时,变量将被“销毁”

鉴于以上两个事实,您的Something实例保持活动状态something变量持有对其的强引用,直到作用域结束,即直到main函数返回。

尝试更换let something = Something() by _ = Something()您会看到您想要的预期行为:

Something.init()
Something.deinit
in main()

当持有实例的弱引用时,会发生相同的行为:weak var something = Something()

请注意,这里有一点细微的差别something变量和Something实例。该变量保存一个参考到实例,而不是实例本身。因此,引用生命周期与变量生命周期相同,这就是影响实例生命周期的因素。为什么变量不尽快销毁,例如当编译器检测到不再需要它时,我不知道。

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

Swift 类在作用域结束时取消初始化,而不是在上次使用后取消初始化 的相关文章

随机推荐

  • 仅从 int 值中删除 csv 文件中的前导零

    我有这个 csv 文件 我试图从中删除前导零 但是使用我的代码来处理任何包含字母或特殊字符的数字 它会清空该值 我不希望这样 column1 column2 column3 column4 column5 column6 column7 c
  • Tkinter 中的文本输入

    Goal 我正在尝试编写一个基本文件 我可以将其导入到所有其他程序中 该程序将具有一个简单的函数 可以从用户那里获取输入然后返回它 Code 为此 我有以下代码 class takeInput object def init self re
  • 在服务中设置线程优先级没有任何效果[关闭]

    Closed 这个问题需要调试细节 目前不接受答案 在 Windows 服务中设置线程优先级之前是否需要一些额外的配置 在我的服务中 我有几个线程 每个线程都调用CreateProcess 启动外部应用程序的函数 我想调整线程 或进程 优先
  • 如何在android中放大/缩小ImageView(不使用Canvas)

    我正在开发一个应用程序 其中我必须在另一个图像 较大的图像 上旋转 拖放和放大 缩小图像 较小的图像 我完成了旋转和拖放 但无法实现放大 缩小 我知道如何在画布上执行此操作 但在我的应用程序中我没有使用画布 任何帮助将不胜感激 谢谢 达文德
  • PHP 对象的动态命名

    如何动态为 php 对象分配名称 例如 我如何将一个对象分配给一个 var 它是我用来创建对象的数据库行的 id 例如
  • 有没有人在windows平台上成功部署到heroku上?

    我一直在阅读各种有关如何从 Windows 在 Heroku 上部署 Rails 应用程序的教程 我尝试过安装 git heroku gem 生成 ssh 密钥并设置路径等等 我收到公钥错误 没有腻子 或致命的未找到身份验证 有腻子 在遇到
  • 使用 FlowLayout 时 JTextField 显示为狭缝...请解释

    有人可以向我解释一下为什么我每次都使用 FlowLayout 布局管理器吗 我的文本字段显示为狭缝 我已经被这个问题撞了一段时间了 我似乎无法弄清楚 找出为什么会出错 我有一种感觉 这是一件简单的事情 我一次又一次地忽视 所以如果 有人可以
  • sklearn 如何计算两个二进制输入的 roc 曲线下的面积?

    我注意到sklearn有以下功能 sklearn metrics roc auc score 它以 ground truth 和预测作为输入 例如 ground truth 1 1 0 0 0 prediction 1 1 0 0 0 sk
  • 地理围栏 - 点在多边形内部/外部

    我想确定一个多边形并实现一个算法来检查一个点是在多边形内部还是外部 有谁知道是否有任何类似算法的示例 如果我没记错的话 该算法是通过测试点画一条水平线 计算出有多少条多边形线相交才能到达您的点 如果答案很奇怪 那么你就在里面 如果答案是偶数
  • 使用长字符串调用 body.search() 时抛出 InvalidArgument 异常

    context document body search 正在扔一个InvalidArgument当要搜索的文本长度超过 255 个字符时 桌面版 Word 2016 中出现异常 Word Online 似乎也是如此 这是失败的代码 var
  • 找出两个 ICollection 集合是否包含相同对象的最快方法

    找出两个是否存在的最快方法是什么ICollection
  • 关闭 Twisted 中的非活动连接

    我正在使用 LineReceiver 协议运行 Twisted 服务器 有时客户端会默默地断开连接 因此 Twisted 会保持连接打开 而且由于除非有请求 否则服务器不会发送任何内容 因此永远不会出现 TCP 超时 换句话说 某些连接在服
  • 设置源数据时删除过渡

    当我将 Windows 8 应用商店应用程序中的默认模板用于项目网格时 在设置集合源时 我会得到一些漂亮的转换 我怎样才能删除这些转换 我正在对数据进行过滤 但我不喜欢每次数据更改时数据都会淡入 您可以通过覆盖 ItemContainerT
  • 如何保护文件免遭未经授权的下载

    我正在使用 PHP 和 MySQL 数据库创建一个会员网站 我有办法让用户使用他们的用户名和密码登录 所有非常标准的东西 我什至安装了系统 允许注册用户下载示例视频文件 这也很好用 然而问题是 任何能够弄清楚文件系统的人都可以直接下载这些文
  • 如何在谷歌地图中设置缩放级别

    这是我编写的代码 用于通过提供纬度和经度向谷歌地图添加标记 问题是我得到了一个非常高缩放的谷歌地图 我尝试将缩放级别设置为 1 但这对高度缩放的地图没有影响
  • Android - 滑动手势出现问题

    我正在尝试在我的应用程序中实现滑动手势 我已经编写了几乎所有代码 但它不起作用 这是我的活动中的代码 Swipe detector gestureDetector new GestureDetector new SwipeGesture t
  • 在 JavaScript 中将 mm-dd-yyyy 转换为日期

    我有 mm dd yyyy 格式的数据 我需要它与 JavaScript 中的当前日期进行比较 我有一些数据 但它不起作用 我的日期是 2016 年 7 月 12 日 格式为 mm dd yyyy var d 12 07 2016 var
  • 重置复选框并删除定义工作表上的注释

    我需要将复选框重置为 FALSE 未选中 并从定义的工作表中删除所有注释 还需要一个从 Google 工作表 所有工作表 中删除所有注释的脚本 我尝试组合许多不同的脚本 https webapps stackexchange com que
  • 蒙古实验室连接错误

    当我尝试使用以下命令连接到 MongoLab 时 收到以下消息 mongo ds035438 mongolab com 35438 comeventsbw u 我的用户名 p 我的密码 MongoDB shell 版本 3 0 6 连接到
  • Swift 类在作用域结束时取消初始化,而不是在上次使用后取消初始化

    我问过这个问题 asking 关于局部变量中引用的生命周期的保证并被提及这个线程其中类实例引用生命周期的确切语义进行了讨论 AFAICT 从该线程来看 类实例可能会在最后一次使用包含最后一个引用的变量之后立即被取消初始化 甚至在同一语句中的