草庐IT

xcode - 正在调用 Swift deinit 但对象仍未解除分配

coder 2023-09-04 原文

在 swift 中,我正在获取 deinit 函数来打印出一行,说明该对象已被取消初始化,但该对象仍在 Instruments 分配工具中报告为事件。我什至认为这是不可能的。有没有办法找出它没有被释放的原因?或者有没有办法找出哪些子对象可以阻止它?

最佳答案

Update: For Swift 4, see the additional note at the end.

Warning: This answer goes into some detail about the way the Swift runtime is implemented. The information here does not affect how you write code in Swift, except in some advanced scenarios. The main point is that from your point of view as a programmer, once deinit is called, the object is dead to you and you cannot use it any more.

内存未被释放的原因是 Swift 中的对象在被取消初始化时不一定会立即释放(释放)。对对象的弱引用将导致对象的“外壳”保持分配状态(在内存中 - 您仍然无法使用它!),直到所有弱引用都被清零。

Swift 中的弱引用在对象被取消初始化时不会立即归零,但在下次访问它们时它们将归零。也就是说,Swift 懒惰地将弱引用归零。这是一个例子:

public class MyClass {
}
var object = MyClass()
weak var weakObject = object
print (weakObject) // Points at a MyClass instance
object = MyClass()
// A: weakObject is not yet nil
print(weakObject) // prints 'nil'
// B: now weakObject is nil

object 分配给新实例后(第 6 行),您会认为对原始对象的弱引用将为零,但它(还)不是。对象被deinited 但保持已分配(在内存中)直到所有弱引用都消失。在 A 点,弱引用仍然存在,它仅在下一行尝试评估 Swift 检查的弱引用并注意到它引用的对象已被取消,因此它将弱引用归零然后将其传递给 print 函数进行打印。这种机制需要对象的空壳保持分配状态,直到所有弱引用都消失为止。它被称为外壳是因为它的所有属性都已在 deinit 中归零并释放,所以它不会保持任何其他事件(对象的内存量非常小,足以存储它的内部 header 和成员)。

为什么以及如何?

每个对象都有一个内部弱引用计数,而不是需要清零的引用列表。这对 deinit 来说更快且占用资源更少,因为以线程安全的方式将弱引用列表置零需要相当长的原子/同步操作。

当强引用计数达到零时,调用deinit,对象进入deallocating状态。运行时保持分配的内存,因为它需要在访问弱引用时检查对象的状态。一旦所有弱引用都被访问并归零(弱引用计数为零),内存将被释放并完成释放。

查看 swift 源代码中 swift_weakLoadStrong 的实现 - 这是在访问弱引用并使其成为强引用时插入的代码(例如分配给强引用或传递给函数等)。我在下面缩写了它。看original code on github查看加载弱引用的全部复杂性:

if (object == nullptr) {
    __atomic_store_n(&ref->Value, (uintptr_t)nullptr, __ATOMIC_RELAXED);
    return nullptr;
}
if (object->refCount.isDeallocating()) {
    __atomic_store_n(&ref->Value, (uintptr_t)nullptr, __ATOMIC_RELAXED);
    SWIFT_RT_ENTRY_CALL(swift_unownedRelease)(object);
    return nullptr;
}
auto result = swift_tryRetain(object);
__atomic_store_n(&ref->Value, ptr, __ATOMIC_RELAXED);
return result;

你可以看到对象壳仍然存在于内存中,加载弱引用的机制(即当你在代码中访问它时)检查它是否正在释放,如果是,它会将弱引用清零,调用 swift_unownedRelease 减少弱引用计数并在计数达到零时释放对象,并返回 nullptr (nil)。

Swift 4 更新

从 Swift 4 开始,弱引用有了改进的实现,这意味着对象外壳不再需要闲逛。相反,使用了一个小边 table ,弱引用实际上指向它。 side-table 包含一个指向真实对象的指针,Swift 知道在访问弱引用时遵循这个指针。有关更详细的解释,请阅读 this great blog post来自 Mike Ash。

关于xcode - 正在调用 Swift deinit 但对象仍未解除分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29019588/

有关xcode - 正在调用 Swift deinit 但对象仍未解除分配的更多相关文章

  1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  2. ruby-on-rails - 按天对 Mongoid 对象进行分组 - 2

    在控制台中反复尝试之后,我想到了这种方法,可以按发生日期对类似activerecord的(Mongoid)对象进行分组。我不确定这是完成此任务的最佳方法,但它确实有效。有没有人有更好的建议,或者这是一个很好的方法?#eventsisanarrayofactiverecord-likeobjectsthatincludeatimeattributeevents.map{|event|#converteventsarrayintoanarrayofhasheswiththedayofthemonthandtheevent{:number=>event.time.day,:event=>ev

  3. ruby-on-rails - 如何验证非模型(甚至非对象)字段 - 2

    我有一个表单,其中有很多字段取自数组(而不是模型或对象)。我如何验证这些字段的存在?solve_problem_pathdo|f|%>... 最佳答案 创建一个简单的类来包装请求参数并使用ActiveModel::Validations。#definedsomewhere,atthesimplest:require'ostruct'classSolvetrue#youcouldevencheckthesolutionwithavalidatorvalidatedoerrors.add(:base,"WRONG!!!")unlesss

  4. Ruby Koans about_array_assignment - 非平行与平行分配歧视 - 2

    通过ruby​​koans.com,我在about_array_assignment.rb中遇到了这两段代码你怎么知道第一个是非并行赋值,第二个是一个变量的并行赋值?在我看来,除了命名差异之外,代码几乎完全相同。4deftest_non_parallel_assignment5names=["John","Smith"]6assert_equal["John","Smith"],names7end45deftest_parallel_assignment_with_one_variable46first_name,=["John","Smith"]47assert_equal'John

  5. Ruby 写入和读取对象到文件 - 2

    好的,所以我的目标是轻松地将一些数据保存到磁盘以备后用。您如何简单地写入然后读取一个对象?所以如果我有一个简单的类classCattr_accessor:a,:bdefinitialize(a,b)@a,@b=a,bendend所以如果我从中非常快地制作一个objobj=C.new("foo","bar")#justgaveitsomerandomvalues然后我可以把它变成一个kindaidstring=obj.to_s#whichreturns""我终于可以将此字符串打印到文件或其他内容中。我的问题是,我该如何再次将这个id变回一个对象?我知道我可以自己挑选信息并制作一个接受该信

  6. ruby-on-rails - 如果 Object::try 被发送到一个 nil 对象,为什么它会起作用? - 2

    如果您尝试在Ruby中的nil对象上调用方法,则会出现NoMethodError异常并显示消息:"undefinedmethod‘...’fornil:NilClass"然而,有一个tryRails中的方法,如果它被发送到一个nil对象,它只返回nil:require'rubygems'require'active_support/all'nil.try(:nonexisting_method)#noNoMethodErrorexceptionanymore那么try如何在内部工作以防止该异常? 最佳答案 像Ruby中的所有其他对象

  7. ruby - 如何在 Lion 上安装 Xcode 4.6,需要用 RVM 升级 ruby - 2

    我实际上是在尝试使用RVM在我的OSX10.7.5上更新ruby,并在输入以下命令后:rvminstallruby我得到了以下回复:Searchingforbinaryrubies,thismighttakesometime.Checkingrequirementsforosx.Installingrequirementsforosx.Updatingsystem.......Errorrunning'requirements_osx_brew_update_systemruby-2.0.0-p247',pleaseread/Users/username/.rvm/log/138121

  8. ruby-on-rails - 未在 Ruby 中初始化的对象 - 2

    我在Rails工作并有以下类(class):classPlayer当我运行时bundleexecrailsconsole然后尝试:a=Player.new("me",5.0,"UCLA")我回来了:=>#我不知道为什么Player对象不会在这里初始化。关于可能导致此问题的操作/解释的任何建议?谢谢,马里奥格 最佳答案 havenoideawhythePlayerobjectwouldn'tbeinitializedhere它没有初始化很简单,因为你还没有初始化它!您已经覆盖了ActiveRecord::Base初始化方法,但您没有调

  9. ruby - 如何在 Rails 4 中使用表单对象之前的验证回调? - 2

    我有一个服务模型/表及其注册表。在表单中,我几乎拥有服务的所有字段,但我想在验证服务对象之前自动设置其中一些值。示例:--服务Controller#创建Action:defcreate@service=Service.new@service_form=ServiceFormObject.new(@service)@service_form.validate(params[:service_form_object])and@service_form.saverespond_with(@service_form,location:admin_services_path)end在验证@ser

  10. 使用 ACL 调用 upload_file 时出现 Ruby S3 "Access Denied"错误 - 2

    我正在尝试编写一个将文件上传到AWS并公开该文件的Ruby脚本。我做了以下事情:s3=Aws::S3::Resource.new(credentials:Aws::Credentials.new(KEY,SECRET),region:'us-west-2')obj=s3.bucket('stg-db').object('key')obj.upload_file(filename)这似乎工作正常,除了该文件不是公开可用的,而且我无法获得它的公共(public)URL。但是当我登录到S3时,我可以正常查看我的文件。为了使其公开可用,我将最后一行更改为obj.upload_file(file

随机推荐