Gab*_*lin 4 ruby destructor finalizer
在 PHP 类中,解析器处理__construct和__destruct方法来实例化实例并在脚本退出或使用 unset 时销毁它。当您扩展一个类时,您只需使用parent::__construct和parent::__destruct来运行可能需要在扩展的类上运行的任何清理代码。
现在,在表示数据库数据并帮助您操作该数据的类的上下文中,我认为__destruct可以使用一种方法来将当前值与从数据库中获取的原始值进行比较,并在必要时进行更新(在某些情况下总是如此)只要主键值无法更改,就进行更新)。在 PHP 中实现这一点非常简单。
这种方法的主要优点是根据需要快速简单地操作类变量,然后让类在最后进行一次大的更新。在运行几分钟的长脚本中,最好在 __construct 期间创建数据库实例、获取数据、关闭数据库连接,然后仅在几分钟的长时间执行期间操作类变量。在 __destruct 上,打开一个新的数据库连接进行更新,然后关闭数据库连接并清理任何其他需要清理的内容。
我很好奇人们对这是否是一个好主意/坏做法的想法,但我的主要问题是这在 Ruby 中是否可能。
在 Ruby 中,您可以在实例化类的实例时运行初始化方法。Ruby 中的等效项parent::__construct是superRuby。Ruby 类有ObjectSpace.define_finalizeand方法。finalize但是,据我了解,finalize 方法不应该能够引用调用它的实例。最重要的是,我找不到任何与parent::__destruct. 我怀疑这是因为没有等效的方法,因为该finalize方法似乎是明确设计来防止引用本身的。
那里有人知道如何做到这一点吗?如果不是,转储 Ruby 类以取回资源并防止数据丢失的最佳实践是什么?是否每个人都有一个在将类实例设置为 nil 之前调用的 Garbage_collection 方法,或者还有其他方法吗?
谢谢
不,Ruby 没有与 PHP 相当的东西__destruct,因为一旦对象的引用计数达到零,PHP 就会销毁该对象,但 Ruby 销毁对象的速度很慢。Ruby 只是偶尔标记和清除物体。另外,Ruby 在扫描 C 代码的局部变量时是保守的。如果 C 局部变量可能指向某个对象,那么 Ruby 会拒绝销毁该对象。
Ruby有ObjectSpace.define_finalizer但是很难用。一个对象可以有多个终结器。如果超类定义了终结器,则可以在同一对象上定义另一个终结器。问题是ObjectSpace.undefine_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:")
Run Code Online (Sandbox Code Playgroud)
我的计算机给出以下输出:
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.
Run Code Online (Sandbox Code Playgroud)
我给 Eve 的年龄加上了 2 岁,但是查数据库之前没有任何更新。解释 Ruby 的 C 代码可能会在某些局部变量中留下对某个局部变量的引用Person.new('Eve'),因此 Ruby 不会销毁该对象。如果您使用其他版本的 Ruby 或不同的 C 编译器,结果可能会改变。当程序退出时,Ruby 确实会运行任何剩余的终结器,因此更新确实发生了,但为时已晚。