Ruby中的终结器:是否有一个等价于__“破坏”;来自PHP?

Ruby中的终结器:是否有一个等价于__“破坏”;来自PHP?,ruby,destructor,finalizer,Ruby,Destructor,Finalizer,在PHP类中,解析器处理\uu构造和\uu析构函数方法来实例化实例并在脚本退出或使用unset时销毁它。扩展类时,只需使用parent::u-construct和parent::u-destruct来运行可能需要在扩展的类上运行的任何清理代码 现在,在一个表示DB数据并帮助您操作该数据的类的上下文中,我认为可以使用\uu destruct方法将当前值与从DB获取的原始值进行比较,并在必要时进行更新(在某些情况下,只要主键值不能更改,就始终执行更新) 这种方法的主要优点是,只需根据需要快速操作类变

在PHP类中,解析器处理
\uu构造
\uu析构函数
方法来实例化实例并在脚本退出或使用unset时销毁它。扩展类时,只需使用
parent::u-construct
parent::u-destruct
来运行可能需要在扩展的类上运行的任何清理代码

现在,在一个表示DB数据并帮助您操作该数据的类的上下文中,我认为可以使用
\uu destruct
方法将当前值与从DB获取的原始值进行比较,并在必要时进行更新(在某些情况下,只要主键值不能更改,就始终执行更新)

这种方法的主要优点是,只需根据需要快速操作类变量,然后让类在最后进行一次大的更新。在运行数分钟的长脚本中,最好在构建期间创建DB实例,获取数据,关闭DB连接,然后仅在数分钟内操作类变量长时间执行。在_destruct上,打开一个新的数据库连接进行更新,然后关闭数据库连接并清理任何需要清理的内容

我很好奇人们对这是否是一个好主意/坏做法有何看法,但我的主要问题是这在Ruby中是否可能

在Ruby中,实例化类的实例时会运行initialize方法。Ruby中的
parent::\u construct
的Ruby等价物是
super
。还有
ObjectSpace。为Ruby类定义\u finalize
finalize
方法。但是,据我所知,finalize方法不是'不应该能够引用调用它的实例。除此之外,我找不到任何与
父级::\u destruct
等效的方法。我怀疑这是因为没有等效的方法,因为
finalize
方法似乎是显式设计来防止引用自身的

有人知道这样做的方法吗?如果没有,转储Ruby类以获取资源和防止数据丢失的最佳实践是什么?每个人都有在将类实例设置为nil之前调用的垃圾收集方法,还是有其他方法


谢谢

正如pst在他的评论中指出的那样,ruby不需要析构函数。只需将所有引用变量设置为null(ref=nil),该对象就会被垃圾收集删除。您无法确切知道它的garbace何时被收集(删除)。此外,您可以(我不建议这样做)编写一个在实际删除该对象之前运行的过程

ObjectSpace.define_finalizer(self, self.class.method(:finalize).to_proc)

不,Ruby与PHP
\uuu destruct
没有等价物,因为PHP在引用计数为零时会销毁对象,但Ruby销毁对象的速度很慢。Ruby只是不时地标记和扫描对象。此外,Ruby在扫描C代码的局部变量时是保守的。如果C局部变量为我指着它

Ruby有
ObjectSpace.define\u finalizer
但很难使用。一个对象可以有多个终结器。如果超类定义了终结器,您可以在同一个对象上定义另一个终结器。问题是
ObjectSpace.Unfine\u finalizer
会从对象中删除所有终结器。因此,如果超类删除如果删除它的终结器,它也会删除你的终结器

Ruby仅在销毁对象后才运行终结器。终结器不能有对对象的引用。如果有,Ruby永远不会销毁对象,并且内存泄漏。终结器不能位于
self
或局部变量引用对象的范围内

在下一个示例中,我将演示如何使用终结器更新数据库。这是一个坏主意,因为Ruby运行终结器的速度很慢。数据库可能很长一段时间都不会更新。我调用
GC.start
,强制进行完全垃圾收集。我的计算机的Ruby拒绝销毁其中一个对象,因此还不会进行一次更新

require 'forwardable'
require 'pstore'

# Create a database of people's ages.
People = PStore.new('people.pstore')
People.transaction do
  People['Alice'] = 20
  People['Bob'] = 30
  People['Carl'] = 40
  People['David'] = 50
  People['Eve'] = 60
end

# Shows people in database.  This can show old values if someone
# forgot to update the database!
def show_people(heading)
  People.transaction(true) do
    puts heading
    %w[Alice Bob Carl David Eve].each do |name|
      puts "  #{name} is #{People[name]} years old."
    end
  end
end

show_people("Before birthdays:")

# This is a person in the database.  You can change his or her age,
# but the database is only updated when you call #update or by this
# object's finalizer.
class Person
  # We need a PersonStruct for the finalizer, because Ruby destroys
  # the Person before calling the finalizer.
  PersonStruct = Struct.new(:name, :age, :oage)
  class PersonStruct
    def update(_)
      s = self
      if s.age != s.oage
        People.transaction { People[s.name] = s.oage = s.age }
      end
    end
  end
  private_constant :PersonStruct

  # Delegate name (r) and age (rw) to the PersonStruct.
  extend Forwardable
  def_delegators(:@struct, :name, :age, :age=)

  # Find a person in the database.
  def initialize(name)
    age = People.transaction(true) { People[name] }
    @struct = PersonStruct.new(name, age, age)
    ObjectSpace.define_finalizer(self, @struct.method(:update))
  end

  # Update this person in the database.
  def update
    @struct.update(nil)
  end
end

# Now give everyone some birthdays.
Person.new('Alice').age += 2
Person.new('Bob').age += 1
Person.new('Carl').age += 1
Person.new('David').age += 1
Person.new('Eve').age += 2

# I forgot to keep references to the Person objects and call
# Person#update.  Now I try to run the finalizers.
GC.start

# Did the database get updated?
show_people("After birthdays:")
我的计算机提供以下输出:

Before birthdays:
  Alice is 20 years old.
  Bob is 30 years old.
  Carl is 40 years old.
  David is 50 years old.
  Eve is 60 years old.
After birthdays:
  Alice is 22 years old.
  Bob is 31 years old.
  Carl is 41 years old.
  David is 51 years old.
  Eve is 60 years old.

我把Eve的年龄增加了2岁,但在检查数据库之前没有更新。解释Ruby的C代码可能留下了对
Person.new('Eve')的引用
在某些局部变量中,因此Ruby不会破坏对象。如果使用其他版本的Ruby或其他C编译器,结果可能会改变。Ruby在程序退出时会运行任何剩余的终结器,因此更新确实发生了,但为时已晚。

Ruby(MRI),就像许多不需要严格内存管理(C++)或不使用引用计数/混合方法(PHP GC)的语言/运行时一样,不支持“析构函数”的概念因此..通常最好重新思考解决方案。详细讨论了以下主题:@ficuscr,我读过论坛上的帖子,认为我的建议可能与Matz/Ruby维护者的设计决策背道而驰。不过我希望有人能找到解决方法。所以我猜答案是Ruby没有办法?@gabe I仍然不明白为什么要处理“析构函数”中的任何内容,更不用说处理数据的任何内容了。如果希望数据被持久化,请告诉它被持久化。例如
context.Update(objectGraph)
object.Save
是两种方法,具体取决于首选模式。Ruby等语言中的析构函数(在MRI和JRuby实现中)的问题在于它不能得到保证。您可以添加closured或ObjectSpace终结器(靠近单独的数据对象[例如,通过组合使用])但是你会遇到麻烦。这不值得。你忘记了3)修复代码以正确地与数据库交互。是的,这听起来是一个很大的工作,但痛苦和痛苦